[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. 3.
  2. 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 }
  3. 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
  4. 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
  5. 11.
  6. 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 周りで頻繁に登場するクラス群
  7. 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 もある
  8. 15.

    Option<A> 値がないことをより安全に扱うため val aruu: String = "HelloWorld" !// ஋͋Γ var

    naii: String? = null !// ஋ͳ͠ val some = Some("HelloWorld") !// ஋͋Γ val none = None !// ஋ͳ͠
  9. 17.

    Option<A> Option#fold 使うことで None/Some の when を簡素化も val bitmap =

    Option.fromNullable(BitmapFactory.decodeStream(!!...)) bitmap.fold({ !// if None },{ !// if Some })
  10. 18.

    Option<A> Option#flatMap, filter, toList…etc なども使えます val numm = Option("123").flatMap {

    try { Some(it.toInt()) } catch (e: NumberFormatException) { None } } !// Some(t=123)
  11. 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>() { !!... }
  12. 22.

    Either<A, B> シンプルな構文例 val eiei = Right("123").map { it.toInt() }

    !// Right(b=123) val eiei2 = Left(IllegalArgumentException("OMG!")) !// Left(a=IllegalArgumentException)
  13. 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) }
  14. 24.

    Either<A, B> If や fold での判定する場合は if (eiei.isLeft) { !!...

    } if (eiei.isRight) { !!... } eiei.fold({ e !-> !// if Left }, { !// if Right })
  15. 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>()
  16. 28.
  17. 29.

    Validated<E, A> If や fold での判定する場合は if (vali.isInvalid) { !!...

    } if (vali.isValid) { !!... } vali.fold({ !// if Invalid }, { !// if Valid })
  18. 30.

    Validated<E, A> Validated#toOption で Option 型として扱う val aaa = Valid(123).toOption()

    !// Some(t=123) val bbb = Invalid(Throwable()).toOption() !// None
  19. 31.

    Validated<E, A> val aaa = Valid(123).toEither() !// Right(b=123) val bbb

    = Invalid(Throwable()).toEither() !// Left(a=Throwable) Validated#toEither で Either 型として扱う
  20. 32.

    Validated<E, A> val aaa = Invalid("abcd").orElse { Valid(123) } val

    bbb = Invalid("abcd").getOrElse { 0 } Validated#orElse, getOrElse で Invalid の場合などに違う値を
  21. 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) }) }
  22. 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
  23. 37.
  24. 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>() {
  25. 40.

    Try<A> シンプルな構文例 val aaa: Try<Int> = Try { 123 }

    val bbb: Try<Int> = Try { throw Throwable("OMG!") }
  26. 41.

    Try<A> If や fold での判定する場合は if (trrr.isFailure) { !!... }

    if (trrr.isSuccess) { !!... } trrr.fold({ e !-> !// if Failure }, { !// if Success })
  27. 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
  28. 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)
  29. 44.

    Try<A> Try#recover で代替の値を設定して Either 型として扱う val aaa: Try<Int> = Try

    { throw Throwable("OMG!") } val bbb = aaa.recover { e !-> 123 }.toEither()
  30. 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 型として扱うなど
  31. 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 { … }
  32. 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 の世界に持ち込む
  33. 57.

    kotlinx.coroutines integration DeferredK { BitmapFactory.decodeFile(!!...) } .runAsync { result !->

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