Pro Yearly is on sale from $80 to $50! »

[JP] Functional Programming in Kotlin with Arrow

[JP] Functional Programming in Kotlin with Arrow

[JP] Functional Programming in Kotlin with Arrow

6dd0483f1353a4a359e92633cfd65c64?s=128

Daichi Furiya (Wasabeef)

September 28, 2018
Tweet

Transcript

  1. Functional Programming in Kotlin with Λrrow wasabeef Shibuya.apk #28

  2. About me Daichi Furiya Google Developer Expert CATS @wasabeef_jp wasabeef

  3. Λrrow

  4. Λrrow は型付き関数型言語を実現 するための Kotlin 向けライブラリ です Λrrow

  5. Λrrow Paco の Twitter より

  6. build.gradle def arrow_version = "0.7.3" !// 2018.9.28 dependencies { implementation

    "io.arrow-kt:arrow-core:$arrow_version" implementation "io.arrow-kt:arrow-syntax:$arrow_version" implementation "io.arrow-kt:arrow-typeclasses:$arrow_version" implementation "io.arrow-kt:arrow-data:$arrow_version" implementation "io.arrow-kt:arrow-instances-core:$arrow_version" implementation "io.arrow-kt:arrow-instances-data:$arrow_version" kapt "io.arrow-kt:arrow-annotations-processor:$arrow_version" implementation "io.arrow-kt:arrow-free:$arrow_version" !// optional implementation "io.arrow-kt:arrow-mtl:$arrow_version" !// optional implementation "io.arrow-kt:arrow-effects:$arrow_version" !// optional implementation "io.arrow-kt:arrow-effects-rx2:$arrow_version" !// optional implementation "io.arrow-kt:arrow-effects-reactor:$arrow_version" !// optional implementation “io.arrow-kt:arrow-effects-kotlinx-coroutines:$arrow_version" !// optional implementation "io.arrow-kt:arrow-optics:$arrow_version" !// optional implementation "io.arrow-kt:arrow-generic:$arrow_version" !// optional implementation "io.arrow-kt:arrow-recursion:$arrow_version" !// optional }
  7. Data Types

  8. Data Types %BUB5ZQFT 0QUJPO &JUIFS 5SZ 7BMJEBUFE /PO&NQUZ-JTU -JTU, 4FRVFODF,

    4FU,  .BQ, 4PSUFE.BQ, *PS *E 3FBEFS ,MFJTJM 4UBUF 4UBUF5 8SJUF5  5SBNQPMJOF $PQSPEVDU &WBM &JUIFS5 'VODUJPO 'VODUJPO $POTU 5ZQF$MBTTFT 4IPX &R 0SEFS 4FNJHSPVQ .POPJE 'PMEBCMF #JGPMEBCMF 5SBWFSTF  3FEVDJCMF 5SBWFSTF'JMUFS 'VODUPS 'VODUPS'JMUFS "QQMJDBUJWF  "QQMJDBUJWF&SSPS .POBE .POBE&SSPS .POBE'JMUFS .POBE3FBEFS  .POBE8SJUFS .POBE4UBUF .POBE$PNCJOF $PNPOBE #JNPOBE  #JGVODUPS 1SPGVODUPS 4FNJHSPVQ, .POBE, *OKFDU "MUFSOBUJWF &⒎FDUT *0 .POBE%FGFS "TZOD &⒎FDU 0QUJDT *TP -FOT 0QUJPOBM 1SJTN (FUUFS 4FUUFS 'PME 5SBWFSTBM "U *OEFY  'JMUFS*OEFY &BDI
  9. Data Types %BUB5ZQFT 0QUJPO &JUIFS 5SZ 7BMJEBUFE /PO&NQUZ-JTU -JTU, 4FRVFODF,

    4FU,  .BQ, 4PSUFE.BQ, *PS *E 3FBEFS ,MFJTJM 4UBUF 4UBUF5 8SJUF5  5SBNQPMJOF $PQSPEVDU &WBM &JUIFS5 'VODUJPO 'VODUJPO $POTU 5ZQF$MBTTFT 4IPX &R 0SEFS 4FNJHSPVQ .POPJE 'PMEBCMF #JGPMEBCMF 5SBWFSTF  3FEVDJCMF 5SBWFSTF'JMUFS 'VODUPS 'VODUPS'JMUFS "QQMJDBUJWF  "QQMJDBUJWF&SSPS .POBE .POBE&SSPS .POBE'JMUFS .POBE3FBEFS  .POBE8SJUFS .POBE4UBUF .POBE$PNCJOF $PNPOBE #JNPOBE  #JGVODUPS 1SPGVODUPS 4FNJHSPVQ, .POBE, *OKFDU "MUFSOBUJWF &⒎FDUT *0 .POBE%FGFS "TZOD &⒎FDU 0QUJDT *TP -FOT 0QUJPOBM 1SJTN (FUUFS 4FUUFS 'PME 5SBWFSTBM "U *OEFY  'JMUFS*OEFY &BDI
  10. Option Either Validated Data Types Try

  11. Option<A>

  12. Option の利点は Null Safty より も安全に値がないことを表現できる こと Option<A>

  13. Option<A> sealed class Option<out A> : OptionOf<A> { !!... }

    object None : Option<Nothing>() { !!... } data class Some<out T>(val t: T) : Option<T>() { !!... } Option 周りで頻繁に登場するクラス群
  14. Option<A> val opt1 = Option("Hello").map { it + "World" }

    !// Option("HelloWorld") val opt2 = Option.fromNullable(null) !// None !// inline fun <B> map(f: (A) !-> B): Option<B> = !// flatMap { a !-> Some(f(a)) } シンプルな構文例 if文などを使わずとも Option#map, flatMap もある
  15. Option<A> 値がないことをより安全に扱うため val aruu: String = "HelloWorld" !// ஋͋Γ var

    naii: String? = null !// ஋ͳ͠ val some = Some("HelloWorld") !// ஋͋Γ val none = None !// ஋ͳ͠
  16. Option<A> when での判定する場合は val bitmap = Option.fromNullable(BitmapFactory.decodeStream(!!...)) when (bitmap) {

    is None !-> { !!... } is Some !-> { !!... } }
  17. Option<A> Option#fold 使うことで None/Some の when を簡素化も val bitmap =

    Option.fromNullable(BitmapFactory.decodeStream(!!...)) bitmap.fold({ !// if None },{ !// if Some })
  18. Option<A> Option#flatMap, filter, toList…etc なども使えます val numm = Option("123").flatMap {

    try { Some(it.toInt()) } catch (e: NumberFormatException) { None } } !// Some(t=123)
  19. Either<A, B>

  20. Either は2つの値のうちの1つを返す可能 性のある戻り値の型をモデル化するための ものであり、 失敗にも値を付加できるの で、何が失敗したか表現できるものです Either<A, B>

  21. Either<A, B> Either 周りで頻繁に登場するクラス群 sealed class Either<out A, out B>

    : EitherOf<A, B> { !!... } data class Left<out A> ɹinternal constructor(val a: A) : Either<A, Nothing>() { !!... } data class Right<out B> ɹinternal constructor(val b: B) : Either<Nothing, B>() { !!... }
  22. Either<A, B> シンプルな構文例 val eiei = Right("123").map { it.toInt() }

    !// Right(b=123) val eiei2 = Left(IllegalArgumentException("OMG!")) !// Left(a=IllegalArgumentException)
  23. Either<A, B> try/catchを使ったシンプルな構文例 val eiei: Either<Throwable, Bitmap> = try {

    Right(BitmapFactory.decodeStream(null)) } catch (e: IllegalArgumentException) { Left(e) } catch (e: OutOfMemoryError) { Left(e) }
  24. Either<A, B> If や fold での判定する場合は if (eiei.isLeft) { !!...

    } if (eiei.isRight) { !!... } eiei.fold({ e !-> !// if Left }, { !// if Right })
  25. Validated<E, A>

  26. Validated は、成功値またはエラー の戻り値をモデル化するためにもの です Validated<E, A>

  27. Validated<E, A> Validated 周りで頻繁に登場するクラス群 sealed class Validated<out E, out A>

    : ValidatedOf<A, B> { !!... } data class Valid<out A>(val a: A) : Validated<Nothing, A>() data class Invalid<out E>(val e: E) : Validated<E, Nothing>()
  28. Validated<E, A> シンプルな構文例 val aaa: Validated<Throwable, Int> = Valid(123) val

    bbb: Validated<Throwable, Int> = Invalid(Throwable("eee"))
  29. Validated<E, A> If や fold での判定する場合は if (vali.isInvalid) { !!...

    } if (vali.isValid) { !!... } vali.fold({ !// if Invalid }, { !// if Valid })
  30. Validated<E, A> Validated#toOption で Option 型として扱う val aaa = Valid(123).toOption()

    !// Some(t=123) val bbb = Invalid(Throwable()).toOption() !// None
  31. Validated<E, A> val aaa = Valid(123).toEither() !// Right(b=123) val bbb

    = Invalid(Throwable()).toEither() !// Left(a=Throwable) Validated#toEither で Either 型として扱う
  32. Validated<E, A> val aaa = Invalid("abcd").orElse { Valid(123) } val

    bbb = Invalid("abcd").getOrElse { 0 } Validated#orElse, getOrElse で Invalid の場合などに違う値を
  33. Semigroup<A>

  34. 複数のエラーを合成する時に使う
 Either ではエラーが発生した時点で中断 してしまうが、Validated は Semigroup などを使ってエラーを集めることができる Semigroup<A>

  35. Semigroup<A> Semigroup 周りで頻繁に登場するクラス群 interface Semigroup<A> { fun A.combine(b: A): A

    operator fun A.plus(b: A): A = this.combine(b) fun A.maybeCombine(b: A?): A = Option.fromNullable(b).fold({ this }, { combine(it) }) }
  36. Semigroup<A> Semigroup 使ってエラーを合成することができる val stringSemi: Semigroup<String> = object : Semigroup<String>

    { override fun String.combine(b: String): String = "$this + $b" } val vavava = Invalid("Hello").findValid(stringSemi) { Invalid("World") } !// vavava.toEither() !=> Left(a=HelloWorld) !// vavava.toOption() !=> None
  37. Try<A>

  38. Tryは、例外が発生する可能性のあ る関数の呼び出しをモデル化するた めのものです Try<A>

  39. Try<A> Try 周りで頻繁に登場するクラス群 sealed class Try<out A> : TryOf<A> {

    !!... } data class Failure(val exception: Throwable) : Try<Nothing>() { data class Success<out A>(val value: A) : Try<A>() {
  40. Try<A> シンプルな構文例 val aaa: Try<Int> = Try { 123 }

    val bbb: Try<Int> = Try { throw Throwable("OMG!") }
  41. Try<A> If や fold での判定する場合は if (trrr.isFailure) { !!... }

    if (trrr.isSuccess) { !!... } trrr.fold({ e !-> !// if Failure }, { !// if Success })
  42. Try<A> Try#toOption で Option 型として扱う val aaa: Try<Int> = Try

    { 123 } aaa.toOption() !// Some(t=123) val bbb: Try<Int> = Try { throw Throwable("OMG!") } bbb.toOption() !// None
  43. Try<A> Try#toEither で Either 型として扱う val aaa: Try<Int> = Try

    { 123 } aaa.toEither() !// Right(b=123) val bbb: Try<Int> = Try { throw Throwable("OMG!") } bbb.toEither() !// Left(a=Throwable)
  44. Try<A> Try#recover で代替の値を設定して Either 型として扱う val aaa: Try<Int> = Try

    { throw Throwable("OMG!") } val bbb = aaa.recover { e !-> 123 }.toEither()
  45. Try<A> val aaa: Try<Int> = Try { throw Throwable("OMG!") }

    val bbb = aaa.recoverWith { e !-> Try { throw OtherException("Oh!!...") } }.recover { e !-> 123 } Try#recoverWith で改めて別の処理を行い、それでも失敗する場合は
 recover で代替の値を設定して Either 型として扱うなど
  46. Option Either Validated Try ここまでのまとめ

  47. Option nullの代わりでより安全にする ここまでのまとめ

  48. Either L, Rの値を持つことができ、エラー発生 時に値を持たせられる。ただしエラー値は1つだけ ここまでのまとめ

  49. Validated Semigroup でエラーを集約できる ここまでのまとめ

  50. Try Throwable を Option や Either に変換する ここまでのまとめ

  51. Option nullの代わりでより安全にする Either L, Rの値を持つことができ、エラー発生 時に値を持たせられる。ただしエラー値は1つだけ Validated Semigroup でエラーを集約できる Try

    Throwable を Option や Either に変換する ここまでのまとめ
  52. Integrations

  53. RxJava 2 Project Reactor kotlinx.coroutines KindedJ Integrations

  54. kotlinx.coroutines

  55. kotlinx.coroutines integration Deferred 周りで頻繁に登場する拡張関数、及びクラス fun <A> Deferred<A>.k(): DeferredK<A> = DeferredK(this)

    data class DeferredK<out A>(val deferred: Deferred<A>) : DeferredKOf<A>, Deferred<A> by deferred { … }
  56. kotlinx.coroutines integration val deferred = async { throw RuntimeException("OMG!") }

    runBlocking { deferred.await() } !// CRASH! Exception! val wrapper = async { throw RuntimeException("OMG!") }.k() val eiei = wrapper.unsafeAttemptSync().toEither() !// Left(a=RuntimeException) Deferred#k() で Deferred をラップすることで Arrow の世界に持ち込む
  57. kotlinx.coroutines integration DeferredK { BitmapFactory.decodeFile(!!...) } .runAsync { result !->

    result.fold({ DeferredK { /** if Throwable **/ } }, { bitmap !-> DeferredK { /** Use the Bitmap **/ } }) } DeferredK は Deferred のArrowラッパーで runAsync は Either を返却してくれる
  58. Conclusion

  59. twitter.com/wasabeef_jp wasabeef.jp github.com/wasabeef