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

2018-10-15-kotlin13

AAkira
October 15, 2018

 2018-10-15-kotlin13

Kotlin Conf2018 報告会
Kotlin1.3新機能
@CA.kt

AAkira

October 15, 2018
Tweet

More Decks by AAkira

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. View Slide

  4. 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.

    View Slide

  5. Agenda
    • Experimental features
    • Contracts
    • New type Inference

    View Slide

  6. λΠτϧ
    Experimental features

    View Slide

  7. Experimental features
    Purpose
    To let new features be tried by early adopters 

    as soon as possible

    View Slide

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

    View Slide

  9. Experimental features
    Migration tool

    View Slide

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

    experimental { coroutines 'enable' }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    fun main() {
    doSomething()
    }

    View Slide

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

    fun main() {
    doSomething()
    }

    View Slide

  19. $POUSBDUT
    Contracts

    View Slide

  20. 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

    View Slide

  21. 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

    View Slide

  22. 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”

    View Slide

  23. 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()

    View Slide

  24. 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()

    View Slide

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

    View Slide

  26. 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()
    コンパイラに一度だけ実行されるという情報を

    教えてあげる

    View Slide

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

    View Slide

  28. Contracts
    Kotlin 1.3より前では実行出来ない
    fun main(args: Array) {

    val s : String? = ""
    if(!s.isNullOrEmpty()) {
    s.first()
    }

    }

    View Slide

  29. Contracts
    Kotlin 1.3より前では実行出来ない
    fun main(args: Array) {

    val s : String? = ""
    if(!s.isNullOrEmpty()) {
    s.first()
    }

    }
    sがnot nullだとはコンパイラは理解出来ない

    View Slide

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

    View Slide

  31. 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の情報を

    コンパイラに教える

    View Slide

  32. Contracts
    Kotlin 1.3では実行可能
    fun main(args: Array) {

    val s : String? = ""
    if(!s.isNullOrEmpty()) {
    s.first()
    }

    }

    View Slide

  33. 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())
    }
    }

    View Slide

  34. 自作も可能
    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が必要

    View Slide

  35. 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で書き換え可能

    View Slide

  36. 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で書き換え可能

    View Slide

  37. 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が不要

    View Slide

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

    View Slide

  39. /FXUZQFJOGFSFODF
    New type inference

    View Slide

  40. New type inference
    kotlin { 

    experimental { newInference 'enable' }
    }

    View Slide

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

    View Slide

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

    })
    }
    .kt

    View Slide

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

    handleInput { println(it) }
    }
    .kt
    1.3からはこう書ける

    View Slide

  44. 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) で回避していた

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  49. 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を使う

    View Slide

  50. 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を使う

    View Slide

  51. 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より前で

    View Slide

  52. 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を省略可能

    View Slide

  53. 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

    View Slide

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

    View Slide

  55. 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
    }

    View Slide

  56. 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
    }

    View Slide

  57. 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と判定しているが…

    View Slide

  58. 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まではコンパイルエラーだった

    View Slide

  59. 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からはコンパイル可

    View Slide

  60. 4VNNBSZ
    Summary

    View Slide

  61. Summary
    • Experimentalの役割がより明確に
    • Kotlin1.3は痒かったところに手が届く

    - Contracts

    - SAM

    - Inference
    • KotlinConf 凄く良かった

    個人的にはKotlin Nativeを激推ししていきたい

    View Slide

  62. AAkira @_a_akira
    Have a nice Kotlin

    View Slide