言いたいことはそれだけか

KotlinとかAndroidとかが好きです。調べたことをメモします。٩( 'ω' )و

Kotlin 1.4からFlowのcombine()がより綺麗に書けるようになった(suspend conversion on callable references)

今回は小ネタ。Kotlinがますます可愛いという話をします。

Kotlin 1.4からsuspend conversion on callable referencesがsupportされました。

(^ もう数ヶ月も前のtweet...)

リリースノートではさらっと

In addition to suspend conversion on lambdas, Kotlin now supports suspend conversion on callable references starting from version 1.4.0.

としか書かれていないのであまり気に留めた人もいないんじゃないかなと思いますが、これによってKotlin coroutineの Flow#combine() がより綺麗に書けるケースがあります。

例えば、二つのflowをcombineして、それをそのまま collect に渡したい場合、1.4より前ではこのような書き方ができませんでした。

suspend fun foo() {
    combine(
        flowA, flowB, ::Pair
    ).collect { (a: Int, b: Int) ->
        println("$a and $b")
    }
}

val flowA = flowOf(1, 2)
val flowB = flowOf(3, 4)

1.4より前だと上記のコードはコンパイルエラーになります。combine()の第3引数はsuspend function lamdbaなので、suspendじゃないPiarクラスのコンストラクタは受け付けてもらえません。1.4からはsuspendに自動で変換してくれるようになったので、上記のコードは問題なく動きます。

ちなみに、collect()の引数のlambdaの引数(a: Int, b: Int)destructuring declarationsを使っていて、いちいちPairのfirst/secondに名前を付け直したりしなくてもここでabといった名前をつけられます。可愛い。

a, bの後ろの型の明示は省略可能ですが、destructuring declarationsを使っているので()はないと動きません。

    combine(
        flowA, flowB, ::Pair
    ).collect { (a, b) -> // OK
        println("$a and $b")
    }
    combine(
        flowA, flowB, ::Pair
    ).collect { a: Int, b: Int -> // NG (コンパイルエラー)
        println("$a and $b")
    }

1.4より前はどういうコードを書いていたか参考までに残しておきます。やはり少し冗長に見えます。

suspend fun fooBefore4_1() {
    combine(
        flowA, flowB
    ) { a, b ->
        a to b
    }.collect {
        val a = it.first
        val b = it.second
        println("$a and $b")
    }
}

val flowA = flowOf(1, 2)
val flowB = flowOf(3, 4)

言いたいことは以上です。