/var/log/jsoizo

メモ帳 技術とか趣味とか

Kotlinで部分適用するためのExtension

部分適用するために以下のようなExtensionを用意しておくと便利。

fun <A, B, C, T> Function3<A, B, C, T>.partial(a: A): (B, C) -> T = {b, c -> invoke(a, b, c)}

例えば以下のような引数を3つ取る関数の場合だとこのようになる。

fun example(foo: Int, bar: String, baz: Long): String = TODO()
val exampleBarBaz = ::example.partial(1)
val result = exampleBarBaz("BarBar", 100L)

関数exampleを ::example としてオブジェクトを参照しているのがポイント。

なお、Function2~4あたりまで何パターンか用意しておくとよいかもしれない。
Function3, 4になると組み合わせが多くなってくるのですべてカバーするべきかは悩ましいところである。

fun <A, B, T> Function2<A, B, T>.partial(a: A): (B) -> T = {b -> invoke(a, b)}
fun <A, B, C, T> Function3<A, B, C, T>.partial(a: A): (B, C) -> T = {b, c -> invoke(a, b, c)}
fun <A, B, C, T> Function3<A, B, C, T>.partial(a: A, b: B): (C) -> T = {c -> invoke(a, b, c)}
fun <A, B, C, D, T> Function4<A, B, C, D, T>.partial(a: A): (B, C, D) -> T = {b, c, d -> invoke(a, b, c, d)}
fun <A, B, C, D, T> Function4<A, B, C, D, T>.partial(a: A, b: B): (C, D) -> T = {c, d -> invoke(a, b, c, d)}
fun <A, B, C, D, T> Function4<A, B, C, D, T>.partial(a: A, b: B, c: C): (D) -> T = {d -> invoke(a, b, c, d)}

Arrow.ktにもそういうExtensionが定義されているのでArrowを使っているプロジェクトならそれも選択肢。
partially1 から partially22 まで存在しており、指定したN番目の引数に対する部分適用がシュッと書けるのが良い点。
そのかわり上記の独自Extensionとは異なり複数の引数に対して部分適用を行うにはちょっと記述量が増えてしまう。

apidocs.arrow-kt.io

fun example(foo: Int, bar: String, baz: Long): String = TODO()
val exampleFooBaz = ::example.partially2("BarBar")
val result = exampleFooBaz(1, 100L)

例えば1番目と3番目の二箇所に対して部分適用したい場合はpartiallyをチェーンさせる。

fun example(foo: Int, bar: String, baz: Long): String = TODO()
fun exampleBar = ::example.partially1(1).partially2(100L)
val result = exampleBar("BarBar")

ちなみにscalaだととてもスッキリ書ける。いいよなぁ。

def example(foo: Int, bar: String, baz: Long): String = ???

val exampleFooBaz = example(_, "BarBar", _)
val result = exampleFooBaz(1, 100L)