Upgrade to Pro — share decks privately, control downloads, hide ads and more …

攻める!ラムダ式禁止おじさん #kotlin_kansai

攻める!ラムダ式禁止おじさん #kotlin_kansai

Kotlin 1.0リリース記念勉強会 in 京都 ( http://kanjava.connpass.com/event/27758/ ) で発表したスライドです。
少し補足を書きました→ http://taro.hatenablog.jp/entry/2016/04/03/173831

Taro Nagasawa

April 02, 2016
Tweet

More Decks by Taro Nagasawa

Other Decks in Programming

Transcript

  1. 自己紹介 • 長澤 太郎 たろーって呼んでね • @ngsw_taro • プログラマー@エムスリー株式会社 ◦

    Android, Kotlin, Java, Scala, Rubyなど • Kotlinエバンジェリスト(JetBrains黙認) ◦ 日本Kotlinユーザグループ代表 ◦ Kotlin入門書 目下執筆中! • やすべえとディズニーが好き
  2. 関数参照 val println: (Int) -> Unit = ::println 123.let(println) 123.let(::println)

    関数オブジェクトへの 参照を取得できる
  3. 引数を2つ取る関数 fun minus(a: Int, b: Int) = a - b

    3.let { minus(5, it) } //=> 2 ここのラムダ式をなくしたい!
  4. デフォルトでカリー化してるっぽく見せる operator fun <A, B, R> ((A, B) -> R).invoke(a:

    A): (B) -> R = { this(a, it) } 「AとBを引数に取り、Rを返す関数」に 拡張関数invokeを生やす
  5. デフォルトでカリー化してるっぽく見せる operator fun <A, B, R> ((A, B) -> R).invoke(a:

    A): (B) -> R = { this(a, it) } Aを引数に取り、 「Bを引数に取り、Rを返す関数」を返す
  6. デフォルトでカリー化してるっぽく見せる operator fun <A, B, R> ((A, B) -> R).invoke(a:

    A): (B) -> R = { this(a, it) } 演算子オーバロードにより function2.invoke(foo) ↓ function2(foo)
  7. デフォルトでカリー化してるっぽく見せる operator fun <A, B, R> ((A, B) -> R).invoke(a:

    A): (B) -> R = { this(a, it) } つまり、invoke関数の存在によって (A, B)->R を (A)->((B)->R) とも見なせる
  8. minusを1引数関数として使える! fun minus(a: Int, b: Int) = a - b

    (::minus)(5, 2) //=> 3 val x = (::minus)(5) x(2) //=> 3 (::minus)(5)(2) //=> 3
  9. 関数の部分適用 5.let { minus(it, 3) } //=> 2 5.let((::minus)(3)) //=>

    -2 5.let((::minus)(_, 3)) //=> 2 プレースホルダーを置いて、 引数の位置をコントロールしたい!
  10. プレースホルダー sealed class PlaceHolder { object アレ : PlaceHolder() }

    型と、その唯一のオブジェクト的な ちなみに、Kotlinでは アンスコのみで構成される名前は使えない
  11. プレースホルダー sealed class PlaceHolder { object アレ : PlaceHolder() }

    object PlaceHolder + import PlaceHolder as アレ でもOK!
  12. プレースホルダーを取る拡張関数invoke operator fun <A, B, R> ((A, B) -> R).invoke(p:

    PlaceHolder, b: B): (A) -> R = { this(it, b) } 「AとBを引数に取り、Rを返す関数」に 拡張関数invokeを生やす
  13. プレースホルダーを取る拡張関数invoke operator fun <A, B, R> ((A, B) -> R).invoke(p:

    PlaceHolder, b: B): (A) -> R = { this(it, b) } プレースホルダーとBを引数に取り、 「Aを取ってRを返す関数」を返す
  14. レシーバが指定されたメソッド参照 // Javaコード static class Calculator { int succ(int x)

    { return x + 1; } } public static void main(final String... args) { final Calculator c = new Calculator(); Optional.of(5).map(c::succ); }
  15. レシーバが指定されたメソッド参照 // Javaコード static class Calculator { int succ(int x)

    { return x + 1; } } public static void main(final String... args) { final Calculator c = new Calculator(); Optional.of(5).map(c::succ); } 「c」のsuccメソッド
  16. 一方、Kotlinは... class Calculator { fun succ(x: Int): Int = x

    + 1 } val calc = Calculator() 2.let(calc::succ) // NG 2.let { calc.succ(it) } // こう書くしか…?
  17. T.(A)->Rは、(T, A)->Rでもある! class Calculator { fun succ(x: Int): Int =

    x + 1 } val a: Calculator.(Int)->Int = Calculator::succ val b: (Calculator, Int)-> Int = a
  18. T.(A)->Rは、(T, A)->Rでもある! class Calculator { fun succ(x: Int): Int =

    x + 1 } val a: Calculator.(Int)->Int = Calculator::succ val b: (Calculator, Int)-> Int = a
  19. つまり val calc = Calculator() 2.let(calc::succ) // NG 2.let {

    calc.succ(it) } // OK 2.let { (Calculator::succ)(calc, it) } // OK
  20. さらに val calc = Calculator() 2.let(calc::succ) // NG 2.let {

    calc.succ(it) } // OK 2.let { (Calculator::succ)(calc, it) } // OK 2.let((Calculator::succ)(calc)) // OK
  21. さらに val calc = Calculator() 2.let(calc::succ) // NG 2.let {

    calc.succ(it) } // OK 2.let { (Calculator::succ)(calc, it) } // OK 2.let((Calculator::succ)(calc)) // OK 既にカリー化する方法を知っているので、 it を省略して、ラムダ式を消せる!
  22. 関数のnullable対応 fun <A, R> ((A) -> R).nullOk(): (A?) -> R?

    = { it?.let(this@nullOk) } 普通の関数を取って
  23. 関数のnullable対応 fun <A, R> ((A) -> R).nullOk(): (A?) -> R?

    = { it?.let(this@nullOk) } 「nullableを取って、 nullableを返す関数」を返す