Scala2.13の新機能をいくつかピックアップ

Scala2.13はコレクションライブラリの再構築がとにかくでかくてそれ以外はどうなんだろ、、と思ってリリースノートを眺めていたところ意外と色々あるなということがわかった。
主に普段の開発ですぐ使えそうな便利機能を拾ってみました。
リリースノート： https://github.com/scala/scala/releases/tag/v2.13.0

ただし網羅性は保証しません、オレオレピックアップであることにご注意を。

# 数値
## 数値リテラルでのアンダースコア区切り
Java7で書けるようになったアレだ。

```scala
val n1: Int = 1_000_000   // 1000000
val n2: Double = 3_14E-2  // 3.14
```

# String

## String interpolatorでマッチしたところを変数に展開
マッチしないとException発生するので注意。

```scala
val s"Hello, $name" = "Hello, James"
println(name)  // "James"

val TimeSplitter = "([0-9]+)[.:]([0-9]+)".r
val s"The time is ${TimeSplitter(hours, mins)}" = "The time is 10.50"
println(hours) // 10
println(mins) // 50

val s"$a-$b-$c" = "1-2-3-4-5-6-7-8-9"
println(a)  // 1
println(b)  // 2
println(c)  // 3-4-5-6-7-8-9

val s"$a-$b-$c" = "foo" // scala.MatchError発生
```

## 文字列から変換できないケースをOptionで安全表現
変換できないケースを考慮してOption型になるメソッドが追加。
Int以外にもDoubleやBooleanなど色々と。

```scala
"123".toIntOption   // Some(123)
"scala".toIntOption // None
```

# Option

## when/unless
条件によってSomeにしたりNoneにしたり、がifなしで書けるように。
以下の例は空文字じゃなければSomeに、空文字ならNoneになるようにするもの。

```scala
def str2Op(s: String): Option[String] = Option.when(!s.isEmpty)(s)

str2Op("hello")  // Some("hello")
str2Op("")       // None
```

なお、whenと条件が逆のunlessもある。
今回の例の場合はunless使えば条件部から `!` が消える。

# Future

## delegateメソッドの追加

メソッド定義は以下。

```scala
def delegate[T](body: => Future[T])(implicit executor: ExecutionContext): Future[T]
```

何が嬉しいんだろう?
以下は例。

常に呼び出し元にはFutureが返ることを保証するために以下のような構造になっているとする。

```scala
trait Foo {
  def run[A](param: Param)(implicit ec: ExecutionContext): Future[A] = {
    // ここでdoRunを呼んで例外発生時はFailureを返す
  }
  protected def doRun[A](param: Param)(implicit ec: ExecutionContext): Future[A]
}
```

例外を補足するコード例は以下。

```scala
try { doRun(param) } catch { case t: Throwable => Future.failed(t) }

Future.fromTry { Try { doRun(param) } }

Future.unit.flatMap{ _ => doRun(param) }

Future(doRun(param)).flatten
```

いずれも、try/Tryを使うとかFuture.unitを一度作るか、flattenを使って2重になっているのを打ち消す等と何かひと手間かかっている事がわかる。
delegateはコレを1発で表現できるようにするメソッドだった。

```scala
Future.delegate(doRun(param))
```

# コレクション

## max/maxBy/min/minByにそれぞれOption型になるメソッドが追加。

```scala
val numbers: Seq[Int] = Nil
numbers.max       // java.lang.UnsupportedOperationException発生
numbers.maxOption // None
```

## 要素数を比較するメソッド
`lengthIs` が追加になった。

```scala
Seq(1,2,3).lengthIs > 5
Seq(1,2,3).lengthIs == 5
// などなど
```

ここでちょっとした疑問。
以下でやってきたはずだけどわざわざ増やしたのはなんで？

```scala
Seq(1,2,3).length > 5
Seq(1,2,3).length == 5
```

lengthを直接呼ぶと要素数が多い場合に全走査して遅くなる。
それを回避するためにもともと `lengthCompare` というメソッドを用意していたもののあまり知られてないっぽいのとこのメソッド使いにくいよね、ということでより使いやすい形で定義したよ、というのが背景のよう。
なおlengthIsの先でlengthCompareが呼ばれてます。

## distinctBy
distinctはあったが、distinctByが追加されてよりきめ細やかに重複除去ができるように。

```scala
Seq(("a", 10), ("b", 20), ("a", 30)).distinctBy(_._1) // Seq(("a", 10), ("b", 20))
```

## partitionMap
Booleanで分割するpartitionはあったが、Eitherを返すpartitionMapが追加。

```scala
val (numbers, strings) =
  Seq(1, "one", 2, "two", 3, "three").partitionMap {
    case n: Int => Left(n)
    case s: String => Right(s)
  }
```

## unfold

新規追加のメソッド。シグニチャは以下。

```scala
def unfold[A, S](init: S)(f: (S) => Option[(A, S)]): List[A]
```

ちょっとわかりにくい。
例を。以下のようなクラスがあったとする。

```scala
case class ResultSetLike[A](private var values: List[A]) {
  def hasNext: Boolean = values.lengthIs > 0
  
  def next: A = {
    val (head :: tail) = values
    values = tail
    head
  }
}

val resultSetLike = ResultSetLike("A" :: "B" :: "C" :: Nil)
```

値を取り出し、String値を作ろうと思うと愚直にやるとこんな感じ。

```scala
val strBuilder = new scala.collection.mutable.StringBuilder()

while(resultSetLike.hasNext) {
  strBuilder.append(resultSetLike.next)
}

val str = strBuilder.toString
```

これをunfoldを使って書き換えると、、、

```scala
val str = 
  List.unfold(resultSetLike){ r => if (r.hasNext) Some(r.next -> r) else None }.mkString
```

となります。

## foreachEntry
`foreach`と違ってFunction2をとるので、 `case (k,v) =>` ってやらずに以下のように`case`なしで使える。

```scala
Map(1->"one", 2->"two").foreachEntry { (k,v) =>
  println(s"key is $k, value is $v")
}
```

# 関数

## PartialFunction同士のandThen/composeが可能に

これまでandThen/composeは継承元のFunction1のメソッドだった。
PartialFunctionでも定義され、PF同士を合成することが出来るように。

```scala
val pf1: PartialFunction[String, Int] = ???
val pf2: PartialFunction[Int, Boolean] = ???
val pf3: PartialFunction[String, Boolean] = pf1 andThen pf2
```

## value discarding警告を回避するUnitの宣言を明示的にする

関数でしか使えないわけではないけどもよく使うのは関数かなと。

以下のコードはコンパイルエラーにならずに通る。

```scala
def foo(): Unit = {
  100
}
```

value discardingというScala言語の機能で以下のようになってる。

```scala
def foo(): Unit = { 
  100
  () 
}
```

この挙動を嫌ってコンパイルエラーにしたい場合は、コンパイラオプションの指定によって振る舞いを変更できる。

```scala:build.sbt
scalacOptions ++= Seq(
  "-Xfatal-warnings",
  "-Ywarn-value-discard"
)
```

当然この設定をすると以下のようなコードを通すには明示的に '()' を最後に返すコードを自分で書く必要がある。毎度コレを書くのもなぁ。

```scala
def do(): Int = ???

def foo(): Unit = {
  ...略...
  do()
  ()
}
```

前置きが長くなったが、そんな時は2.13でサポートされた以下の記述でちょっとだけｼｭｯと書けるようになった。

```scala
def foo(): Unit = {
  ...略...
  do(): Unit
}
```

# その他

## tap/pipe

Optionだとmapで変換関数を渡し、foreachでUnitな副作用関数を渡す、といった処理順で記述することが出来るわけですが、

```scala
val f = (_:Int) * 10

Option(1 + 2 + 3).map(f).foreach(println)
```

一方、これが生のIntになると処理順で記述しようとすると変数にとった複数行に渡る記述になるし、一行で記述しようとすると処理順とは逆の記述をしないといけなくなる。

```scala
val n1 = 1 + 2 + 3
val n2 = f(n1)
println(n2)

// もしくは
println(f(1 + 2 + 3))
```

pipe/tapを使うと以下のように記述できるようになる。

```scala
import scala.util.chaining._

(1 + 2 + 3).pipe(f).tap(println)
```

なお、tapをOption#foreachと対比させるような記述をしましたが、tapの戻り値はUnitではなく入力値を返すことに注意。

```scala
def tap[U](f: A => U): A = {
  f(self)
  self
}
```

## Using

リソースの自動リリース（Close処理）が可能。
`java.lang.AutoCloseable` を持ったリソースを対象とする。

```scala
val lines: Try[Seq[String]] =
  Using(new BufferedReader(new FileReader("file1.txt"))) { reader =>
    import scala.jdk.StreamConverters._
    reader.lines.toScala(List)
  }
```

複数リソースを伴う処理はこんな感じ。

```scala
val lines2: Try[Seq[String]] =
  Using.Manager { use =>
    val r1 = use { new BufferedReader(new FileReader("file1.txt")) }
    val r2 = use { new BufferedReader(new FileReader("file2.txt")) }

    r1.lines.toScala(List) :::  r2.lines().toScala(List)
  }
```

## unusedアノテーション

scalacオプションに「-Xfatal-warnings -Ywarn-unused」を設定していると使用していない変数について警告を出してくれる。

ただし中には使用を忘れているわけではないものも警告が出てしまう問題があった。
（例として以下のような例外の種類によってリトライするかどうかを指定できる仕組みの実装において通常はリトライはしない、みたいなデフォルトの設定をするような時）

```scala
trait Base {
  protected def isRetry(t: Throwable): Boolean = false
  
  ...略...
}
```

こういう場合はunusedアノテーションを使うことで意図的に使ってないことを宣言でき、警告を出さないようにできる。

```scala
import annotation.unused

protected def isRetry(@unused t: Throwable): Boolean = false
```

## Product
productElementNameおよびproductElementNamesが追加になった。こんな感じでケースクラスのフィールド名が取れるようになった。

```scala
case class Param(token: String, userId: String) {
  def urlParam =
    (productElementNames zip productIterator).map{ case (k, v) => s"$k=$v" }.mkString("&")
}

Param("xxxx-yyyy-zzzz", "mtoyoshi").urlParam  // token=xxxx-yyyy-zzzz&userId=mtoyoshi
````