Slide 1

Slide 1 text

AAkira @_a_akira Kotlin Conf2018報告会 - Kotlin 1.3新機能-

Slide 2

Slide 2 text

About me @_a_akira AAkira CyberAgent, Inc. Akira Aratani https://aakira.app !"#$%&'()*+,-./012345

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

New Type Inference and Related Language Features Svetlana Isakova Svetlana Isakova has worked on the Kotlin language at JetBrains. She’s co-authored the book Kotlin in Action and now is a Developer Advocate, where she spends her time teaching Kotlin and speaking at conferences worldwide.

Slide 5

Slide 5 text

Agenda • Experimental features • Contracts • New type Inference

Slide 6

Slide 6 text

λΠτϧ Experimental features

Slide 7

Slide 7 text

Experimental features Purpose To let new features be tried by early adopters 
 as soon as possible

Slide 8

Slide 8 text

Experimental features Tools > Kotlin > Enable migrations detection (experimental)

Slide 9

Slide 9 text

Experimental features Migration tool

Slide 10

Slide 10 text

Experimental features experimentalの機能を使う場合はgradleに追加するする必要がある kotlin { 
 experimental { coroutines 'enable' } }

Slide 11

Slide 11 text

Experimental features import kotlinx.coroutines.experimental.* import kotlinx.coroutines.* ˣ 1.2 1.3

Slide 12

Slide 12 text

Experimental features 自分でExperimental APIを作ることも可能 @Experimental annotation class ShinyNewAPI

Slide 13

Slide 13 text

Experimental features // Ϋϥεʹ΋෇͚ΒΕΔ @ShinyNewAPI class Foo {} // ؔ਺ʹ΋෇͚ΒΕΔ @ShinyNewAPI fun Foo.bar() = ""

Slide 14

Slide 14 text

Experimental features @ShinyNewAPI fun doSomething() { val foo = Foo() foo.bar() } @ShinyNewAPI fun main() { doSomething() }

Slide 15

Slide 15 text

Experimental features @ShinyNewAPI fun doSomething() { val foo = Foo() foo.bar() } @ShinyNewAPI fun main() { doSomething() } 1.3からmain関数の引数はオプションになった

Slide 16

Slide 16 text

Experimental features @ShinyNewAPI fun doSomething() { val foo = Foo() foo.bar() } @ShinyNewAPI fun main() { doSomething() }

Slide 17

Slide 17 text

Experimental features @UseExperimental(ShinyNewAPI::class) fun doSomething() { val foo = Foo() foo.bar() } 
 fun main() { doSomething() }

Slide 18

Slide 18 text

Experimental features @UseExperimental(ShinyNewAPI::class) fun doSomething() { val foo = Foo() foo.bar() } 
 fun main() { doSomething() }

Slide 19

Slide 19 text

$POUSBDUT Contracts

Slide 20

Slide 20 text

Kotlin Puzzler Kotlin1.2で実行する場合 fun main(args: Array) { var hoge: Int? run { hoge = 46 } println(hoge) } // a) 46 // b) NullPointerException // c) null // d) Will not compile

Slide 21

Slide 21 text

Kotlin Puzzler Kotlin 1.3より前では実行出来ない fun main(args: Array) { var hoge: Int? run { hoge = 46 } println(hoge) } // a) 46 // b) NullPointerException // c) null // d) Will not compile

Slide 22

Slide 22 text

Kotlin Puzzler fun main(args: Array) { var hoge: Int? run { hoge = 46 } println(hoge) } // a) 46 // b) NullPointerException // c) null // d) Will not compile コンパイラは初期化されているのか、判断出来ない 
 printの部分で “variable hoge must be initialized”

Slide 23

Slide 23 text

Contracts // 1.3 inline fun run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } // 1.2
 public inline fun run(block: () -> R): R = block()

Slide 24

Slide 24 text

Contracts // 1.3 inline fun run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } // 1.2
 public inline fun run(block: () -> R): R = block()

Slide 25

Slide 25 text

Contracts • can be implicitly changed • can implicitly break code depending on it なぜコンパイラはrun内で代入されたhogeがnon-nullなのを理解出来ないのか

Slide 26

Slide 26 text

Contracts // 1.3 inline fun run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } // 1.2
 public inline fun run(block: () -> R): R = block() コンパイラに一度だけ実行されるという情報を
 教えてあげる

Slide 27

Slide 27 text

Contracts Kotlin 1.3では実行可能 fun main(args: Array) { var hoge: Int? run { hoge = 46 } println(hoge) }

Slide 28

Slide 28 text

Contracts Kotlin 1.3より前では実行出来ない fun main(args: Array) {
 val s : String? = "" if(!s.isNullOrEmpty()) { s.first() }
 }

Slide 29

Slide 29 text

Contracts Kotlin 1.3より前では実行出来ない fun main(args: Array) {
 val s : String? = "" if(!s.isNullOrEmpty()) { s.first() }
 } sがnot nullだとはコンパイラは理解出来ない

Slide 30

Slide 30 text

Contracts 1.3でのisNullOrEmptyの実装 public inline fun CharSequence?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) } return this == null || this.length == 0 }

Slide 31

Slide 31 text

Contracts 1.3でのisNullOrEmptyの実装 public inline fun CharSequence?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) } return this == null || this.length == 0 } return falseの時に
 isNullOrEmpty != nullの情報を
 コンパイラに教える

Slide 32

Slide 32 text

Contracts Kotlin 1.3では実行可能 fun main(args: Array) {
 val s : String? = "" if(!s.isNullOrEmpty()) { s.first() }
 }

Slide 33

Slide 33 text

Contracts 自作も可能 @ExperimentalContracts private inline fun CharSequence?.hoge(): Boolean { contract { returns(false) implies (this@hoge != null) } return this == null || this.length == 0 }
 
 @ExperimentalContracts fun main() { var a: String? = "abc" if (!a.hoge()) { println(a.first()) } }

Slide 34

Slide 34 text

自作も可能 Contracts @ExperimentalContracts private inline fun CharSequence?.hoge(): Boolean { contract { returns(false) implies (this@hoge != null) } return this == null || this.length == 0 }
 
 @ExperimentalContracts fun main() { var a: String? = "abc" if (!a.hoge()) { println(a.first()) } } contractもExperimentalContracts annotationが必要

Slide 35

Slide 35 text

Contracts @UseExperimental(ExperimentalContracts::class) private inline fun CharSequence?.hoge(): Boolean { contract { returns(false) implies (this@hoge != null) } return this == null || this.length == 0 } fun main() { var a: String? = "abc" if (!a.hoge()) { println(a.first()) } } UseExperimentalで書き換え可能

Slide 36

Slide 36 text

Contracts @UseExperimental(ExperimentalContracts::class) private inline fun CharSequence?.hoge(): Boolean { contract { returns(false) implies (this@hoge != null) } return this == null || this.length == 0 } fun main() { var a: String? = "abc" if (!a.hoge()) { println(a.first()) } } ここでも、UseExperimental annotationが使える UseExperimentalで書き換え可能

Slide 37

Slide 37 text

Contracts @UseExperimental(ExperimentalContracts::class) private inline fun CharSequence?.hoge(): Boolean { contract { returns(false) implies (this@hoge != null) } return this == null || this.length == 0 } fun main() { var a: String? = "abc" if (!a.hoge()) { println(a.first()) } } annotationが不要

Slide 38

Slide 38 text

Contracts • run, let, with, apply, also, takeIf, takeUnless, synchronized • isNullOrEmpty, isNullOrBlank, • kotlin.test: assertTrue, assertFalse, assertNotNull • check, checkNotNull, require, requireNotNull Contractが追加されたメソッド達

Slide 39

Slide 39 text

/FXUZQFJOGFSFODF New type inference

Slide 40

Slide 40 text

New type inference kotlin { 
 experimental { newInference 'enable' } }

Slide 41

Slide 41 text

New type inference • SAM conversions for Kotlin functions • Better inference for builders • Better inference for call chains • Better inference for intersection types

Slide 42

Slide 42 text

public interface Action { void execute(T target); } SAM conversions .java fun handleInput(handler: Action) { ... } fun main() { handleInput(Action { target -> println(target) 
 }) } .kt

Slide 43

Slide 43 text

public interface Action { void execute(T target); } SAM conversions .java fun handleInput(handler: Action) { ... } fun main() {
 handleInput { println(it) } } .kt 1.3からはこう書ける

Slide 44

Slide 44 text

SAM conversions val obs = Observable.just(100) Observable.just(1) .zipWith(obs, BiFunction { a, b -> a + b }) .subscribe { } RxJava2から第二引数でlamda式が使えなくなっていた
 => RxKotlin(https://github.com/ReactiveX/RxKotlin) で回避していた

Slide 45

Slide 45 text

SAM conversions val obs = Observable.just(100) Observable.just(1) .zipWith(obs) { a, b -> a + b } .subscribe { } RxKotlinを使わなくても良くなった!

Slide 46

Slide 46 text

Inference for builder lambdas val seq = sequence { yield(42) print("hoge") yield(4) } Kotlin1.3から型パラメータが不要

Slide 47

Slide 47 text

Inference for builder lambdas Kotlin1.3から型パラメータが不要 val seq = sequence { yield(42) print("hoge") yield(4) }

Slide 48

Slide 48 text

Inference for builder lambdas fun buildList( init: MutableList.() -> Unit ): List { return mutableListOf().apply(init) } fun main() { val list = buildList { add(1) add(2) } } もちろん自分でも定義可能

Slide 49

Slide 49 text

Inference for builder lambdas @UseExperimental(ExperimentalTypeInference::class) fun buildList( @BuilderInference init: MutableList.() -> Unit ): List { return mutableListOf().apply(init) } fun main() { val list = buildList { add(1) add(2) } } BuilderInference annotationを使う

Slide 50

Slide 50 text

Inference for builder lambdas @UseExperimental(ExperimentalTypeInference::class) fun buildList( @BuilderInference init: MutableList.() -> Unit ): List { return mutableListOf().apply(init) } fun main() { val list = buildList { add(1) add(2) } } BuilderInference annotationを使う

Slide 51

Slide 51 text

Inference for builder lambdas @UseExperimental(ExperimentalTypeInference::class) fun buildList( @BuilderInference init: MutableList.() -> Unit ): List { return mutableListOf().apply(init) } fun main() { val list = buildList { add(1) add(2) } } これによって1.3より前で

Slide 52

Slide 52 text

Inference for builder lambdas @UseExperimental(ExperimentalTypeInference::class) fun buildList( @BuilderInference init: MutableList.() -> Unit ): List { return mutableListOf().apply(init) } fun main() { val list = buildList { add(1) add(2) } } Intを省略可能

Slide 53

Slide 53 text

Inference for call chains fun createMap(): Map = MapBuilder() .put("answer", 42) .build() MapBuilderは自分で定義した関数? (発表資料ままです) Kotlin 1.2: One call is analyzed at a time
 Kotlin 1.3: A call chain is analyzed

Slide 54

Slide 54 text

Inference for call chains fun createMap() = MapBuilder() .put("answer", 42) .build() Kotlin1.3からは全体で評価されるので型定義を省略可能

Slide 55

Slide 55 text

Intersection types interface Foo interface Bar fun hoge(thing: T) where T : Foo, T : Bar { println(thing) } fun better() { val something: Better = Better.A if (something is Foo && something is Bar) { hoge(something) // Foo & Bar } } sealed class Better { object A : Better() object B : Better(), Foo object C : Better(), Foo, Bar }

Slide 56

Slide 56 text

Intersection types interface Foo interface Bar fun hoge(thing: T) where T : Foo, T : Bar { println(thing) } fun better() { val something: Better = Better.A if (something is Foo && something is Bar) { hoge(something) // Foo & Bar } } sealed class Better { object A : Better() object B : Better(), Foo object C : Better(), Foo, Bar }

Slide 57

Slide 57 text

Intersection types interface Foo interface Bar fun hoge(thing: T) where T : Foo, T : Bar { println(thing) } fun better() { val something: Better = Better.A if (something is Foo && something is Bar) { hoge(something) // Foo & Bar } } sealed class Better { object A : Better() object B : Better(), Foo object C : Better(), Foo, Bar } somethingは Foo & Barと判定しているが…

Slide 58

Slide 58 text

Intersection types interface Foo interface Bar fun hoge(thing: T) where T : Foo, T : Bar { println(thing) } fun better() { val something: Better = Better.A if (something is Foo && something is Bar) { hoge(something) // Compile error } } sealed class Better { object A : Better() object B : Better(), Foo object C : Better(), Foo, Bar } Kotlin1.2まではコンパイルエラーだった

Slide 59

Slide 59 text

Intersection types interface Foo interface Bar fun hoge(thing: T) where T : Foo, T : Bar { println(thing) } fun better() { val something: Better = Better.A if (something is Foo && something is Bar) { hoge(something) // Foo & Bar } } sealed class Better { object A : Better() object B : Better(), Foo object C : Better(), Foo, Bar } 1.3からはコンパイル可

Slide 60

Slide 60 text

4VNNBSZ Summary

Slide 61

Slide 61 text

Summary • Experimentalの役割がより明確に • Kotlin1.3は痒かったところに手が届く
 - Contracts
 - SAM
 - Inference • KotlinConf 凄く良かった
 個人的にはKotlin Nativeを激推ししていきたい

Slide 62

Slide 62 text

AAkira @_a_akira Have a nice Kotlin