Kotlin の標準関数覚え書き(Scala との比較等)

9ヶ月ほどサーバーサイド Kotlin を書いて過ごしていましたが、そろそろ Scala の仕事に戻れそうなので、備忘録として両者の書き味の違いを残して置こうと思います。
Collection とか Iterable 周りの関数とか書き方を中心に書きます。


## 実行環境など

Kotlin をラフに書いて試したい場合は、IntelliJ の Kotlin REPL を使うのが良さそうです。今回実行したバージョンはこちら。

```shell:Kotlin
Welcome to Kotlin version 1.4.21 (JRE 13.0.1+9)
```

https://pleiades.io/help/idea/kotlin-repl.html



Scala の場合は`scala`コマンドか`sbt console`を使うと思います。今回実行したバージョンはこちら。

```shell:Scala
Welcome to Scala 2.13.5 (OpenJDK 64-Bit Server VM, Java 15.0.2).
```


## Collections の関数

### Scala には同等の関数がなさそうなやつ

Kotlin の関数を調べると、今のところ Scala には定義されていない便利そうな関数がたくさんあって面白かったので、Scala を使った場合の同等な処理と書き比べる形で紹介しようと思います。

```kotlin:Kotlin
val ints: List<Int> = (1..3).toList()
```

```scala:Scala
val ints: Seq[Int] = 1 to 3
```

コード例にはこちらの変数を使います。

#### associate(associateWith, associateBy)

Scala では配列から`Map`を作るとき`Tuple2` + `toMap`をすると思います。(キーが被らない保証があるとき等)

```scala:Scala
ints.map(i => i -> s"user$i").toMap
// val res0: scala.collection.immutable.Map[Int,String] = Map(1 -> user1, 2 -> user2, 3 -> user3)
```

Kotlin なら`associate`という関数が用意されています。

```kotlin:Kotlin
ints.associate { it to "user$it" }
// res0: kotlin.collections.Map<kotlin.Int, kotlin.String> = {1=user1, 2=user2, 3=user3}
```

なお`associateWith`, `associateBy`とかを使うとシンプルに書けることがあります。

#### filterIsInstance

Scala では型でフィルタリングする際は、`collect`を使って型のパターンマッチをして

```scala:Scala
Seq(List(1), Set(2), Map(3 -> 4)).collect { case xs: Set[Int] => xs }
// val res1: Seq[Set[Int]] = List(Set(2))
```

のように書くと思います。少しトリッキーな書き方になってしまいますよね。
Kotlin なら`filterIsInstance`という関数が用意されています。

```kotlin:Kotlin
listOf(listOf(1), setOf(2), mapOf(3 to 4)).filterIsInstance<Set<Int>>()
// res1: kotlin.collections.List<kotlin.collections.Set<kotlin.Int>> = [[2]]
```

#### groupingBy

Kotlin には Scala と同じ様に`groupBy`関数も用意されていますが、似たような名前の`groupingBy`という関数も存在します。この関数の返り値は`Grouping`型という集計処理を行うための中間型になっていて、例えば`groupby` + `count`のような処理をしたい場合は、以下のように書く事ができます。

```kotlin:Kotlin
ints.groupingBy { it % 2 == 0 }.eachCount()
// res2: kotlin.collections.Map<kotlin.Boolean, kotlin.Int> = {false=2, true=1}
```

Scala で同等のことがしたい場合は`groupBy` + `mapValues`, または`groupMapReduce `を使って

```scala:Scala
ints.groupBy(_ % 2 == 0).view.mapValues(_.size).toMap
// val res2: scala.collection.immutable.Map[Boolean,Int] = Map(false -> 2, true -> 1)

ints.groupMapReduce(_ % 2 == 0)(_ => 1)(_ + _)
// val res3: scala.collection.immutable.Map[Boolean,Int] = Map(false -> 2, true -> 1)
```

という風に書く必要があります。少し冗長になるのと、やってることがなんか少し分かりづらそうですよね。(あと遅そう)
`Grouping`型を使いこなせたらスマートに書ける処理がたくさんありそうですね。

#### none

Scala では「条件に合致するものが一個もない」と判定したい場合`exists`を反転して

```scala:Scala
!ints.exists(_ % 2 == 0)
// val res4: Boolean = false
```

などとやると思います。
Kotlin の場合、`none`という関数が用意されています。

```kotlin:Kotlin
ints.none { it % 2 == 0 }
// res4: kotlin.Boolean = false
```

#### union

Scala にも同名の関数がありますが、deprecated になっているのと、処理内容が違うので注意が必要です。Kotlin の`union`は配列を合体させて一意にしたものが返されます。

```kotlin:Kotlin
ints.union(2..5)
// res5: kotlin.collections.Set<kotlin.Int> = [1, 2, 3, 4, 5]
```

同じ様な処理を Scala で書きたい場合、`concat` + `toSet`するとそれっぽくなると思います。

```scala:Scala
ints.concat(2 to 5).toSet
// val res5: scala.collection.immutable.Set[Int] = HashSet(5, 1, 2, 3, 4)
```

#### takeLastWhile(dropLastWhile)

Scala の場合、配列の逆から `takeWhile`するような関数がないので、やろうとすると少しブサイクですが、`reverse`して`reverse`する感じで

```scala:Scala
ints.reverse.takeWhile(1 < _).reverse
// val res6: Seq[Int] = Range 2 to 3
```

この様に書きますかね。(もう少しなんとかなりそうな気はするものの)
Kotlin には`**LastWhile`という関数が用意されています。

```kotlin:Kotlin
ints.takeLastWhile { 1 < it }
// res6: kotlin.collections.List<kotlin.Int> = [2, 3]
```

#### zipWithNext

Scala では配列の次の要素と`zip`したい場合`tail` + `zip`で書きますよね。

```scala:Scala
ints.zip(ints.tail)
// val res7: Seq[(Int, Int)] = Vector((1,2), (2,3))
```

Kotlin には`zipWithNext`という関数が用意されています。

```kotlin:Kotlin
ints.zipWithNext()
// res7: kotlin.collections.List<kotlin.Pair<kotlin.Int, kotlin.Int>> = [(1, 2), (2, 3)]
```

#### average

Scala では数値の平均を出すには`Double`にして配列のサイズで割ると思います。

```scala:Scala
ints.sum.toDouble / ints.length
// val res8: Double = 2.0
```

Kotlin には`average`という関数が用意されています。

```kotlin:Kotlin
ints.average()
// res8: kotlin.Double = 2.0
```

#### mapIndexed(filterIndexed, foldIndexed...)

Scala では index 値を使って配列を操作したい場合`zipWithIndex`と組み合わせて書くと思います。

```scala:Scala
ints.zipWithIndex.map { case (index, i) => s"$i-$index" }
// val res9: Seq[String] = Vector(0-1, 1-2, 2-3)
```

Kotlin には`**Indexed`という合成済みの関数が用意されています。

```kotlin:Kotlin
ints.mapIndexed { index, i -> "$i-$index" }
// res9: kotlin.collections.List<kotlin.String> = [1-0, 2-1, 3-2]
```

#### sortedDescending

Scala では逆順にソートする場合`sorted.reverse`もしくはパフォーマンスが気になる場合は

```scala:Scala
ints.sorted(Ordering[Int].reverse)
// val res10: Seq[Int] = Vector(3, 2, 1)
```

このように書いたりすると思います。
Kotlin には`sortedDescending`という関数が用意されています。

```kotlin:Kotlin
ints.sortedDescending()
// res10: kotlin.collections.List<kotlin.Int> = [3, 2, 1]
```

#### maxOf(minOf, sumOf...)

Scala では`map`した結果の`max`をとる処理を書く場合、文字通り以下のように書きます。

```scala:Scala
ints.map(i => i * i % 5).max
// val res11: Int = 4
```

Kotlin には`**Of`という関数が用意されています。

```kotlin:Kotlin
ints.maxOf { it * it % 5 }
// res11: kotlin.Int = 4
```

#### runningReduce

Scala で「初期値を与えない`scan`」みたいなことをしたいとき、僕だったら配列が空か判断してから以下のようにやるかなと思います。

```scala:Scala
ints.tail.scan(ints.head)(_ + _)
// val res12: Seq[Int] = Vector(1, 3, 6)
```

Kotlin では以下のように書けます。

```kotlin:Kotlin
ints.runningReduce { acc, i -> acc + i }
// res12: kotlin.collections.List<kotlin.Int> = [1, 3, 6]
```

`scan`を使ってて「累積和の最初の 0 いらないなぁ」って`tail`したことある気がするので、そんなときこれを使うと便利かもしれないですね。

### Scala と同等な関数があるけど別名のやつ

他の言語間でもそうだと思いますが、同じ処理なのに関数名が違うものが色々ありますので紹介します。

#### all, any

配列の要素に全部に対して判定をする様な関数で、Scala では、`forall`, `exists`に対応する関数が、`all`, `any`になります。上の方で紹介した`none`もこの関数と同じ括り。

```kotlin:Kotlin
ints.all { it % 2 == 0 }
// res0: kotlin.Boolean = false

ints.any { it % 2 == 0 }
// res1: kotlin.Boolean = true
```

```scala:Scala
ints.forall(_ % 2 == 0)
// val res0: Boolean = false

ints.exists(_ % 2 == 0)
// val res1: Boolean = true
```

#### intersect, subtract

文字通り、積集合、差集合を作ることのできる関数で、Scala では、`intersect`, `diff`が対応します。`intersect`は同じなのに、差集合は違う名前なんですね。上で紹介した`union`もこの関数と同じ括り。

Kotlin の方は返り値の型が`Set`になるようなので、元の情報が消えてしまうことがある気がします。

```kotlin:Kotlin
ints.intersect(2..4)
// res2: kotlin.collections.Set<kotlin.Int> = [2, 3]

ints.subtract(2..4)
// res3: kotlin.collections.Set<kotlin.Int> = [1]
```

```scala:Scala
ints.intersect(2 to 5)
// val res2: Seq[Int] = Vector(2, 3)

ints.diff(2 to 5)
// val res3: Seq[Int] = Vector(1)
```

#### windowed, chunked

Scala では`sliding`, `grouped`という関数がありますが、それに対応する関数が Kotlin では `windowed`, `chunked`です。(返り値の型とか、`grouped`は`slidning`の一般化した関数ではないとか色々あると思いますが、やれることは一緒)

配列をいい感じに分割して(二次元配列にして)返す処理が書けます。

```scala:Scala
ints.grouped(2).toList
// val res4: List[Seq[Int]] = List(Range 1 to 2, Range 3 to 3)

ints.sliding(2, 2).toList
// val res5: List[Seq[Int]] = List(ArraySeq(1, 2), ArraySeq(3))
```

```kotlin:Kotlin
ints.chunked(2)
// res4: kotlin.collections.List<kotlin.collections.List<kotlin.Int>> = [[1, 2], [3]]

ints.windowed(2, 2, true)
// res5: kotlin.collections.List<kotlin.collections.List<kotlin.Int>> = [[1, 2], [3]]
```

`windowed`は、第三引数の`partialWindows: Boolean`を`false`にすると、size に足りない要素は消してくれる機能付きです。(デフォルトが`false`なので注意が必要です)

```kotlin:Kotlin
ints.windowed(2, 2)
// res6: kotlin.collections.List<kotlin.collections.List<kotlin.Int>> = [[1, 2]]
```

#### runningFold

これは`scan`そのものです。Kotlin にも`scan`関数が用意されているんですが、それも`runningFold`を呼び出す形で定義されています。

```kotlin:_Collections.kt
public inline fun <T, R> Iterable<T>.scan(initial: R, operation: (acc: R, T) -> R): List<R> {
    return runningFold(initial, operation)
}
```

```kotlin:Kotlin
ints.runningFold(10) { acc, i -> acc + i }
// res7: kotlin.collections.List<kotlin.Int> = [10, 11, 13, 16]
```

```scala:Scala
ints.scan(10)(_ + _)
// val res7: Seq[Int] = Vector(10, 11, 13, 16)
```

#### in, !in

こちらは関数名ではなく`contains`の糖衣構文ですが Scala では出来ない書き方なので紹介します。

```kotlin:Kotlin
2 in ints
// res8: kotlin.Boolean = true

2 !in ints
// res9: kotlin.Boolean = false
```

```scala:Scala
ints.contains(2)
// val res8: Boolean = true

!ints.contains(2)
// val res9: Boolean = false
```

**[2021/05/04追記ここから]**

### Scala と同名だけど注意が必要なやつ

#### map, filter, flatMap...

がくぞさんにお教えいただいたので、追記します。

https://twitter.com/gakuzzzz/status/1389237994142371842?s=20

> KotlinはSet等でmap/filterしてもListになりますが(なので別途mapToやfilterToがあります)

灯台下暗しという感じでした。。`map`などで型が変わらない方が普通、みたいな感覚があったので全然意識外でした。試してみると、

```scala:Scala
Set(1, 2, 3).map(_ % 2)
// val res0: scala.collection.immutable.Set[Int] = Set(1, 0)
```

```kotlin:Kotlin
setOf(1, 2, 3).map { it % 2 }
// res0: kotlin.collections.List<kotlin.Int> = [1, 0, 1]
```

Scala の場合は`Set`のままだけど、Kotlin の場合は`List`になっています。

```kotlin:_Collections.kt
fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R>
```

定義を見るとそりゃそうですが`List`を返すようになっていますね。他の配列を返す標準関数もだいたい`List`を返す様に出来ているので、注意が必要そうです。注意が必要そうなことの一例でいうと、ものすごく巨大な配列に対して操作したあとに結果として`Set`が欲しい場合など、

```kotlin:Kotlin
hugeSet.map { it % 2 }.toSet()
```

とやると、`List`から`Set`へのコンバート処理にすごいコストが掛かって、めちゃ遅いということが起きると思います。

ですのでそういうときは mutable にまわせるようにしたいというニーズに答えられるように`**To`のような、第一引数に`MutableCollection`を受け取る別バージョンも用意している、ということかと思いました。

**[2021/05/04追記ここまで]**

### null 系関数

Scala と Kotlin では`null`の扱いが違っており Scala では`Option`型として表現されるのに対して、Kotlin では各型に対して`?`という`null`を許容すること(`nullability`)を表す記号を書きます。
そのため Kotlin には Scala には存在しない「`null`のことを考えた関数」がたくさん用意されています。

#### takeIf, takeUnless

`takeIf`は受け取った関数の判定が真の場合、自身を返して(型には`?`がつく)、偽の場合は、`null`を返すというものです。`takeUnless`はその逆ですね。

```kotlin:Kotlin
2.takeIf { it % 2 == 0 }
// res0: kotlin.Int? = 2

2.takeUnless { it % 2 == 0 }
// res1: kotlin.Int? = null
```

Scala と比べるのは不毛ですが、近い関数は `Option::when`, `Option::unless`になると思います。

```scala:Scala
val x = 2
// val x: Int = 2

Option.when(x % 2 == 0)(x)
// val res0: Option[Int] = Some(2)

Option.unless(x % 2 == 0)(x)
// val res1: Option[Int] = None
```

#### firstOrNull, singleOrNull, elementAtOrNull

`**OrNull`系の関数は、指定した要素が見つからなかった(ではなかった)場合に`null`を返してくれる関数です。

```kotlin:Kotlin
ints.firstOrNull()
// res2: kotlin.Int? = 1

ints.singleOrNull()
// res3: kotlin.Int? = null

ints.elementAtOrNull(100)
// res4: kotlin.Int? = null
```

これも Scala と比べるのは不毛ですが近い処理を書くと、

```scala:Scala
ints.headOption
// val res2: Option[Int] = Some(1)

Option.when(ints.lengthIs == 1)(ints.head)
// val res3: Option[Int] = None

Option.when(ints.isDefinedAt(100))(ints(100))
// val res4: Option[Int] = None
```

とかになりますでしょうか。
あんまりスマートに書けないですね。なにかいい書き方あればお教えいただきたい・・・。

**[2021/05/04 追記ここから]**

がくぞさんに、お教えいただいたので追記します。

https://twitter.com/gakuzzzz/status/1389237533133836289?s=20

```scala:Scala
ints.lift(1)
// val res0: Option[Int] = Some(2)

ints.lift(100)
// val res1: Option[Int] = None
```

`PartialFunction`に属する関数なんですね。全然見たことなかったので勉強になりました！

**[2021/05/04 追記ここまで]**

#### mapNotNull, filterNotNull, listOfNotNull

`**NotNull`系はその名の通り、`null`を除外してくれる合成済みの関数という感じです。

```kotlin:Kotlin
ints.mapNotNull { it.takeIf { it % 2 == 0 } }
// res5: kotlin.collections.List<kotlin.Int> = [2]

listOf(1, 2, null, 3, 4).filterNotNull()
// res6: kotlin.collections.List<kotlin.Int> = [1, 2, 3, 4]

listOfNotNull(1, 2, null, 3, 4)
// res7: kotlin.collections.List<kotlin.Int> = [1, 2, 3, 4]
```

Scala の場合は、`flatten`, `flatMap`とかで`Option`を潰す処理と同等になるかと思います。

#### as?

`as` は型変換をする際に使うものですが、`as?`とすると、変換に失敗したときは例外ではなく`null`になってくれるというものです。便利ですね。

```kotlin:Kotlin
10000000000L as? Int
// res8: kotlin.Int? = null
```

Scala で同じ様な処理書くとしたら、汎用的には書けなそうな気がしますがどうなんでしょうか。上記と似た様な処理は、一応こんな感じで書けると思います。

```scala:Scala
Option.when(10000000000L.isValidInt)(10000000000L.toInt)
// val res8: Option[Int] = None
```

## Range の違い

範囲を表す`Range`は Kotlin では以下のように書きます。

```kotlin:Kotlin
// 1 ~ 10 の配列を作る場合
1..10
// res0: kotlin.ranges.IntRange = 1..10

// 1 ~ 9 (10 を含まない)の配列を作る場合
1 until 10
// res1: kotlin.ranges.IntRange = 1..9

// 10 ~ 1 の配列を作る場合
10 downTo 1
// res2: kotlin.ranges.IntProgression = 10 downTo 1 step 1
```

`downTo`を使った場合は、`IntRange`の親クラスである`IntProgression`として作られます。また`IntRange`という型からわかるように、他の型の`Range`も個別で用意されています。

#### Long や Double の Range

`Long`型や`Double`型の場合も同じ様に書くことができますが、`Double`型については注意が必要です。

```kotlin:Kotlin
// Long 型の場合
1L..10L
// res3: kotlin.ranges.LongRange = 1..10

// Double 型の場合
1.1..1.9
// res4: kotlin.ranges.ClosedFloatingPointRange<kotlin.Double> = 1.1..1.9

res4.contains(1.7)
// res5: kotlin.Boolean = true

res4.contains(2.0)
// res6: kotlin.Boolean = false
```
`Long`の場合、`LongRange`になるのは分かりやすいんですが、`Double`の場合の`ClosedFloatingPointRange`とはなんでしょうか。
調べてみるとイテレータとしての機能はないけど、`contains`, `isEmpty`などのメソッドは持っている型のようです。

Kotlin では小数に対してイテレートする`Range`は定義できなそうですが、

```kotlin:Kotlin
(1..3).map { it / 10.0 }
// res7: kotlin.collections.List<kotlin.Double> = [0.1, 0.2, 0.3]
```

必要な場合は、上記のように`map`すればいいから特に困らなそうですね。

一方 Scala では以下の様に書きます。

```scala:Scala
// 1 ~ 10 の配列を作る場合
1 to 10
// res0: scala.collection.immutable.Range.Inclusive = Range 1 to 10

// 1 ~ 9 (10 を含まない)の配列を作る場合
1 until 10
// res1: scala.collection.immutable.Range = Range 1 until 10

// 10 ~ 1 の配列を作る場合
10 to 1 by -1
// res2: scala.collection.immutable.Range = Range 10 to 1 by -1
```

こちらは、引数が一個の`to`を使った場合だけ、`Range`の子クラスである`Range.Inclusive`として作られます。Kotlin の`downTo`の様な関数はないので、`10 to 1 by -1`とする必要があります。

#### Long や Double の Range

Scala の場合`Long`型は書けますが、`Double`型については書くことができません。代替案として`BigDecimal`型で step を小数にすることで小数の`Range`自体は実現できます。

```scala:Scala
// Long 型の場合
1L to 10L
// res3: scala.collection.immutable.NumericRange.Inclusive[Long] = NumericRange 1 to 10

// Double 型の場合
9.474 to 49.474 by 1.0
scala> 9.474 to 49.474 by 1.0
             ^
       error: value to is not a member of Double

// BigDecimal 型　の場合
BigDecimal(9.474) to BigDecimal(49.474) by 0.1
// res5: scala.collection.immutable.NumericRange.Inclusive[scala.math.BigDecimal] = NumericRange 9.474 to 49.474 by 0.1
```

なお Scala では`1 to 10`という書き方は、`Range`を生成するときに使いますが、Kotlin では`Pair`を生成するときに使うので注意が必要です。`until`は Scala, Kotlin で書き方一緒なので、余計にややこしいですね。

## Pair の違い

`Collection`でも`Iterable`でもないですが、よく使うデータ構造である`Pair`を見ていきます。

### オブジェクト生成

Kotlin の場合、`Pair`の生成は以下のとおりです。

```kotlin:Kotlin
Pair(1, "user1")
// res0: kotlin.Pair<kotlin.Int, kotlin.String> = (1, user1)

1 to "user1"
// res1: kotlin.Pair<kotlin.Int, kotlin.String> = (1, user1)
```

Scala の場合は、`Pair`ではなく`Tuple2`ですが、以下のようにインスタンスを生成します。

```scala:Scala
(1, "user1")
// res0: (Int, String) = (1,user1)

1 -> "user1"
// res1: (Int, String) = (1,user1)
```

Scala では、`->`は`Tuple2`を生成するときに使う糖衣構文ですが、Kotlin ではラムダやパターンマッチのときに使う記号です。Scala ではラムダやパターンマッチをするときに使うのは`=>`なので、混乱しがちです。

### 関数

Kotlin の`Pair`には(`Triple`もですが)、`toList`メソッドが用意されています。

```kotlin:Triple.kt
public fun <T> Pair<T, T>.toList(): List<T> = listOf(first, second)
```

first, second が同じ型の場合に使えるものなので、以下のように使えます。

```kotlin:Kotlin
Pair(1, 2).toList()
// res3: kotlin.collections.List<kotlin.Int> = [1, 2]
```

Scala の`Tuple2`には、`toList`メソッドは用意されていないので、同等の処理を書こうとすると、

```scala:Scala
(1, 2).productIterator.collect { case i: Int => i }.toList
// val res3: List[Int] = List(1, 2)
```

の様になります。標準で`toList`欲しい。

なお Scala 3 だと`Tuple`周りがすごく書きやすくなりそうなので、とても楽しみにしています。(`toList`もできるようになるみたいです。)

https://www.scala-lang.org/2021/02/26/tuples-bring-generic-programming-to-scala-3.html

## Scala にあるけど Kotlin にはなさそうな関数

たくさんあると思いますが「なんでないんだ。不便だな。」と思った関数だけ上げていきます。
これまでとは逆に Kotlin だったらどう書くかも併せて書いていこうと思います。

#### collect

Scala だと`collect`という`filter` + `map`の役割を持つ関数があるんですが Kotlin にはなさそうでした。

```scala:Scala
ints.collect{ case i if i % 2 == 0 => s"$i is even" }
// val res0: Seq[String] = Vector(2 is even)
```

なので Kotlin の場合、`filter` + `map`するしかないんでしょう。おそらく。

```kotlin:Kotlin
ints.filter { it % 2 == 0 }.map { "$it is even" }
// res0: kotlin.collections.List<kotlin.String> = [2 is even]
```

#### transpose

Scala には、`transpose`という、二次元配列を「縦で持ち直す」便利な関数があるんですが、Kotlin にはなさそうでした。

```scala:Scala
Seq(Seq(1, 2, 3), Seq(11, 12, 13), Seq(21, 22, 23)).transpose
// val res1: Seq[Seq[Int]] = List(List(1, 11, 21), List(2, 12, 22), List(3, 13, 23))
```

Kotlin で書く場合、`indices`を使って自身を`map`する処理を書くしかなさそう。

```kotlin:Kotlin
val list = arrayOf(arrayOf(1, 2, 3), arrayOf(11, 12, 13), arrayOf(21, 22, 23))
list.first().indices.map { i -> list.map { it[i] } }
// res1: kotlin.collections.List<kotlin.collections.List<kotlin.Int>> = [[1, 11, 21], [2, 12, 22], [3, 13, 23]]
```

他には、`splitAt`, `span`, `inits`, `tails`などの関数も Kotlin にはないので自力で実装しなきゃいけなくて、欲しくなる場面があるだろうなと思いました。

### 終わりに

Kotlin, Scala それぞれ個性があって面白かったです。ちょっとしたロジックを書く分にはこのくらい覚えておいて、また使うときにちょっとアップデートできればいいかなと思っています。

今回はほとんどコレクション周りの標準関数の書き比べしか書いていませんが、Scala から Kotlin を見ると

- `Option`がない
- `Either`がない
- パターンマッチ弱い

みたいなこと(もっとあると思うけど)で実装方針が全然変わってくるので、それぞれのコーディング手法の良し悪しの理解が進んだら、そちらについても書き残しておきたいなと思っています。