Slide 1

Slide 1 text

ラムダ式禁止おじさん 〜もうすぐver 1.1が来るバージョン〜 2016-12-13 第4回Kotlin勉強会@Sansan 長澤 太郎 @ngsw_taro

Slide 2

Slide 2 text

Kotlinってラムダ式使えて快適〜(^q^) list .filter { it % 3 == 0 } .fold(0) { a, e -> a + e } .let { println(it) }

Slide 3

Slide 3 text

おじさん曰く

Slide 4

Slide 4 text

ラムダ式は 難しい! おじさん曰く

Slide 5

Slide 5 text

複数の文が入り込む余地があるのが... list .filter { hoge(); fuga(); piyo(); it % 3 == 0 } .fold(0) { a, e -> a + e } .let { println(it) }

Slide 6

Slide 6 text

ラムダ式なんか禁止だ〜〜〜〜〜!!! list .filter(myCondition) .fold(0, Int::plus) .let(::println)

Slide 7

Slide 7 text

トリにして ネタ枠 です

Slide 8

Slide 8 text

もくじ 1. 関数参照・メソッド参照 2. カリー化、関数の部分適用 3. Java的なメソッド参照 4. nullableどうするの問題 5. まとめと注意事項

Slide 9

Slide 9 text

自己紹介 ● 長澤 太郎 たろーって呼んでね ○ @ngsw_taro ● エンジニア@エムスリー株式会社 ○ ITで医療を変革! ○ Android, Kotlin, Java, Spring, Railsなど ● Kotlinエバンジェリスト ● ディズニーが好き

Slide 10

Slide 10 text

1. 関数参照・メソッド参照

Slide 11

Slide 11 text

引数を1つだけ取る関数 123.let { println(it) }

Slide 12

Slide 12 text

引数を1つだけ取る関数 123.let { println(it) } ここのラムダ式をなくしたい!

Slide 13

Slide 13 text

関数参照 val println: (Int) -> Unit = ::println 123.let(println) 123.let(::println)

Slide 14

Slide 14 text

関数参照 val println: (Int) -> Unit = ::println 123.let(println) 123.let(::println) 関数オブジェクトへの 参照を取得できる

Slide 15

Slide 15 text

関数参照 val println: (Int) -> Unit = ::println 123.let(println) 123.let(::println) 関数の型

Slide 16

Slide 16 text

関数参照 val println: (Int) -> Unit = ::println 123.let(println) 123.let(::println) 普通に引数として渡すだけ

Slide 17

Slide 17 text

関数参照 val println: (Int) -> Unit = ::println 123.let(println) 123.let(::println) もちろん直接もOK!

Slide 18

Slide 18 text

引数を取らないメソッド "hoge".let { it.toUpperCase() }

Slide 19

Slide 19 text

引数を取らないメソッド "hoge".let { it.toUpperCase() } ここのラムダ式をなくしたい!

Slide 20

Slide 20 text

メソッド参照 val toUpperCase: String.()->String = String::toUpperCase "hoge".let(toUpperCase)

Slide 21

Slide 21 text

メソッド参照 val toUpperCase: String.()->String = String::toUpperCase "hoge".let(toUpperCase) 関数オブジェクトへの 参照を取得できる

Slide 22

Slide 22 text

メソッド参照 val toUpperCase: String.()->String = String::toUpperCase "hoge".let(toUpperCase) 関数の型

Slide 23

Slide 23 text

メソッド参照 val toUpperCase: String.()->String = String::toUpperCase "hoge".let(toUpperCase) レシーバの型

Slide 24

Slide 24 text

メソッド参照 val toUpperCase: String.()->String = String::toUpperCase "hoge".let(toUpperCase) 引数リスト->戻り型

Slide 25

Slide 25 text

メソッド参照 val toUpperCase: String.()->String = String::toUpperCase "hoge".let(toUpperCase) 普通に引数として渡すだけ

Slide 26

Slide 26 text

できたこと 123.let { println(it) } 123.let(::println) "hoge".let { it.toUpperCase() } "hoge".let(String::toUpperCase)

Slide 27

Slide 27 text

2. カリー化、関数の部分適用

Slide 28

Slide 28 text

引数を2つ取る関数 fun minus(a: Int, b: Int) = a - b 3.let { minus(5, it) } //=> 2 ここのラムダ式をなくしたい!

Slide 29

Slide 29 text

デフォルトでカリー化してるっぽく見せる operator fun ((A, B) -> R).invoke(a: A): (B) -> R = { this(a, it) }

Slide 30

Slide 30 text

Slide 31

Slide 31 text

Slide 32

Slide 32 text

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

ラムダ式を消せる! 3.let { minus(5, it) } //=> 2 3.let((::minus)(5)) //=> 2

Slide 37

Slide 37 text

引数の位置を変えたい問題 5.let { minus(it, 3) } //=> 2 5.let((::minus)(3)) //=> -2

Slide 38

Slide 38 text

関数の部分適用 5.let { minus(it, 3) } //=> 2 5.let((::minus)(3)) //=> -2 5.let((::minus)(_, 3)) //=> 2 プレースホルダーを置いて、 引数の位置をコントロールしたい!

Slide 39

Slide 39 text

プレースホルダー enum class PlaceHolder { アレ }

Slide 40

Slide 40 text

プレースホルダーを取る拡張関数invoke operator fun ((A, B) -> R).invoke(p: PlaceHolder, b: B): (A) -> R = { this(it, b) }

Slide 43

Slide 43 text

1引数バージョンのminusを自在に使える (::minus)(5, 3) //=> 2 (::minus)(アレ, 3)(5) //=> 2

Slide 44

Slide 44 text

ラムダ式を消せる! 5.let { minus(it, 3) } //=> 2 5.let((::minus)(アレ, 3)) //=> 2

Slide 45

Slide 45 text

3. Java的なメソッド参照

Slide 46

Slide 46 text

レシーバが指定されたメソッド参照 // 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); }

Slide 47

Slide 47 text

レシーバが指定されたメソッド参照 // 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メソッド

Slide 48

Slide 48 text

一方、Kotlinは... class Calculator { fun succ(x: Int): Int = x + 1 } val calc = Calculator() 2.let(calc::succ) // NG 2.let { calc.succ(it) } // こう書くしか…?

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

つまり val calc = Calculator() 2.let(calc::succ) // NG 2.let { calc.succ(it) } // OK 2.let { (Calculator::succ)(calc, it) } // OK

Slide 52

Slide 52 text

さらに 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

Slide 53

Slide 53 text

さらに 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 を省略して、ラムダ式を消せる!

Slide 54

Slide 54 text

ちょっと待った!! class Calculator { fun succ(x: Int): Int = x + 1 } val calc = Calculator() 2.let(calc::succ) // NG 2.let { calc.succ(it) } // こう書くしか…?

Slide 55

Slide 55 text

ちょっと待った!! class Calculator { fun succ(x: Int): Int = x + 1 } val calc = Calculator() 2.let(calc::succ) // OK ver1.1から 2.let { calc.succ(it) } // こう書くしか…? ラムダ式禁止が捗るね!!

Slide 56

Slide 56 text

4. nullableどうするの問題

Slide 57

Slide 57 text

安全呼び出しするためには、ラムダ式が... setOf(2).map { it?.let(::succ) } setOf(2).map { it?.inc() }

Slide 58

Slide 58 text

安全呼び出しするためには、ラムダ式が... setOf(2).map { it?.let(::succ) } setOf(2).map { it?.inc() }

Slide 59

Slide 59 text

Slide 62

Slide 62 text

ラムダ式を消せる setOf(2).map { it?.let(::succ) } setOf(2).map { it?.inc() } setOf(2).map(::succ.nullOk()) setOf(2).map(Int::inc.nullOk())

Slide 63

Slide 63 text

5. まとめと注意事項

Slide 64

Slide 64 text

まとめ ● 関数参照、メソッド参照により、スッキリしたコードが書ける ● カリー化や関数の部分適用により、無理やり1引数関数の形 にして、ラムダ式を排除できる ● Java的なメソッド参照のような記法は使えない。しかし、無理 やり1引数関数の形にして、ラムダ式を排除できる →ver1.1 から可能に! ● notNullを取る関数を持ち上げて、無理やりnullableに対応さ せ、ラムダ式を排除できる

Slide 65

Slide 65 text

注意事項 ● ラムダ式を使いましょう ● 「単純な関数参照」はOK ● ちょっと複雑な関数参照は、インライン展開されない場合が多 く、パフォーマンスに影響あり ● わざわざトリッキーなコード書くな(怒)

Slide 66

Slide 66 text

Thank you Enjoy Kotlin!