Save 37% off PRO during our Black Friday Sale! »

ContractsでCompilerを賢くしよう / love_kotlin #5

80a3a3857a55f154d23acb705eff72cc?s=47 star_zero
October 24, 2018

ContractsでCompilerを賢くしよう / love_kotlin #5

80a3a3857a55f154d23acb705eff72cc?s=128

star_zero

October 24, 2018
Tweet

Transcript

  1. Contractsで Compilerを賢くしよう Kotlin愛好会 vol5

  2. About me •Kenji Abe •Cookpad Inc. •Android Developer •Twitter: @STAR_ZERO

  3. Contracts •Kotlin 1.3の新機能 •関数の動作を明示的にCompilerへ伝える •スマートキャストをより便利に

  4. 今回の環境 •IntelliJ IDEA 2018.2.5 •Kotlin 1.3.0-rc-190

  5. 簡単な例

  6. Contractsなし fun isNonNull(s: String?): Boolean { return s != null

    } fun hoge(s: String?) { if (isNonNull(s)) { // Smart CastされないのでCompile Error println(s.length) } }
  7. Contractsあり @ExperimentalContracts fun isNonNull(s: String?): Boolean { contract { returns(true)

    implies (s != null) } return s != null }
  8. Contractsあり @ExperimentalContracts fun hoge(s: String?) { if (isNonNull(s)) { //

    Smart Castしてくれる println(s.length) } }
  9. Contractsなし fun runFunc(func: () -> Unit) { func() } fun

    hoge() { var x: Int runFunc { x = 10 } // Variable 'x' must be initialized println(x) }
  10. Contractsあり @ExperimentalContracts fun runFunc(func: () -> Unit) { contract {

    callsInPlace(func, InvocationKind.EXACTLY_ONCE) } func() }
  11. Contractsあり @ExperimentalContracts fun hoge() { var x: Int runFunc {

    x = 10 } // エラーなし println(x) }
  12. returns - implies

  13. returns - implies @ExperimentalContracts fun isNonNull(s: String?): Boolean { contract

    { returns(true) implies (s != null) } return s != null } •戻り値によって決まる引数またはthisの状態を定義
  14. returns - implies •returns() •returns(value: Any?) ‣ true, false, null

    •returnsNotNull()
  15. returns - implies •implies ‣ == null, != null ‣

    is, !is
  16. Example 1 @ExperimentalContracts fun validate(s: String?) { contract { returns()

    implies (s != null) } if (s.isNullOrEmpty()) { throw IllegalArgumentException() } }
  17. Example 1 @ExperimentalContracts fun hoge(value: String?) { validate(value) println("length =

    ${value.length}") }
  18. Example 2 @ExperimentalContracts fun isString(v: Any?): Boolean { contract {

    returns(true) implies (v is String) } return v is String }
  19. Example 2 @ExperimentalContracts fun hoge(v: Any?) { if (isString(v)) {

    println("length = ${v.length}") } }
  20. Example 3 @ExperimentalContracts fun castInt(v: Any?): Int? { contract {

    returnsNotNull() implies (v is Int) } return v as? Int }
  21. Example 3 @ExperimentalContracts fun hoge(v: Any?) { if (castInt(v) !=

    null) { println("${v + v}") } }
  22. Example 4 @ExperimentalContracts fun Any?.isString(): Boolean { contract { returns(true)

    implies (this@isString is String) } return this is String }
  23. Example 4 @ExperimentalContracts fun hoge(v: Any?) { if (v.isString()) {

    println("length = ${v.length}") } }
  24. Example 5 @ExperimentalContracts fun stringOrInt(v: Any?) : Boolean { contract

    { returns(true) implies (v is String) returns(false) implies (v is Int) } return v is String }
  25. Example 5 @ExperimentalContracts fun hoge(v: Any?) { if (stringOrInt(v)) {

    println("length = ${v.length}") } else { println("plus = ${1 + v}") } }
  26. callsInPlace

  27. callsInPlace @ExperimentalContracts fun runFunc(func: () -> Unit) { contract {

    callsInPlace(func, InvocationKind.EXACTLY_ONCE) } func() } •引数の関数が何回実行されるか
  28. callsInPlace •AT_MOST_ONCE ‣ 1回実行されるか、呼び出されない •AT_LEAST_ONCE ‣ 1回以上呼び出される •EXACTLY_ONCE ‣ 1回だけ呼び出される

    •UNKNOWN ‣ 呼び出される回数が不明
  29. Example 1 @ExperimentalContracts fun runAtLeastOnce(func: () -> Unit) { contract

    { callsInPlace(func, InvocationKind.AT_LEAST_ONCE) } func() }
  30. Example 1 @ExperimentalContracts fun hoge() { var x: Int runAtLeastOnce

    { x = 10 } println(x) }
  31. Example 2 @ExperimentalContracts fun runAtMostOnce(func: () -> Unit) { contract

    { callsInPlace(func, InvocationKind.AT_MOST_ONCE) } func() }
  32. Example 2 @ExperimentalContracts fun hoge() { var x: Int runAtMostOnce

    { x = 10 } // Variable 'x' must be initialized println(x) }
  33. Example 3 @ExperimentalContracts fun runFuncs(func1: () -> Unit, func2: ()

    -> Unit) { contract { // func1だけ設定 callsInPlace(func1, InvocationKind.EXACTLY_ONCE) } func1() func2() }
  34. Example 3 @ExperimentalContracts fun hoge() { var x: Int runFuncs({

    x = 10 }) { x = 20 } // Variable 'x' must be initialized println(x) }
  35. Example 4 @ExperimentalContracts fun runFuncs(func1: () -> Unit, func2: ()

    -> Unit) { contract { // 今度はfunc2だけ設定 callsInPlace(func2, InvocationKind.EXACTLY_ONCE) } func1() func2() }
  36. Example 4 @ExperimentalContracts fun hoge() { var x: Int runFuncs({

    x = 10 }) { x = 20 } println(x) }
  37. Example 5 @ExperimentalContracts fun runFunc(func: () -> Unit) { contract

    { callsInPlace(func, InvocationKind.EXACTLY_ONCE) } func() }
  38. Example 5 @ExperimentalContracts fun hoge() { var x: Int runFunc({

    x = 10 }) // <- 括弧で括る // Variable 'x' must be initialized println(x) }
  39. Example 6 @ExperimentalContracts fun complex(v: Any?, func: () -> Unit):

    Boolean { contract { returns(true) implies (v is String) callsInPlace(func, InvocationKind.EXACTLY_ONCE) } func() return v is String }
  40. Example 6 @ExperimentalContracts fun hoge(v: Any) { var x: Int

    if (complex(v) { x = 10 }) { println("length = ${v.length}") } println("x = $x") }
  41. 注意

  42. 注意 class Helper { @ExperimentalContracts fun isNonNull(s: String?): Boolean {

    // Contracts are allowed only for top-level functions contract { returns(true) implies (s != null) } return s != null } } •Top-Level関数でしか使えない
  43. まとめ

  44. まとめ •Contractsを使うとComplierがちょっと賢くなる •拡張関数で使うのが現実的な気がする •今後に期待?

  45. ありがとうございました