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

[JP] Functional Programming in Kotlin with Arrow

[JP] Functional Programming in Kotlin with Arrow

[JP] Functional Programming in Kotlin with Arrow

Avatar for Daichi Furiya (Wasabeef)

Daichi Furiya (Wasabeef)

September 28, 2018
Tweet

More Decks by Daichi Furiya (Wasabeef)

Other Decks in Programming

Transcript

  1. 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 }
  2. 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
  3. 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. Option<A> sealed class Option<out A> : OptionOf<A> { !!... }

    object None : Option<Nothing>() { !!... } data class Some<out T>(val t: T) : Option<T>() { !!... } Option 周りで頻繁に登場するクラス群
  5. 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 もある
  6. Option<A> 値がないことをより安全に扱うため val aruu: String = "HelloWorld" !// ஋͋Γ var

    naii: String? = null !// ஋ͳ͠ val some = Some("HelloWorld") !// ஋͋Γ val none = None !// ஋ͳ͠
  7. Option<A> Option#fold 使うことで None/Some の when を簡素化も val bitmap =

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

    try { Some(it.toInt()) } catch (e: NumberFormatException) { None } } !// Some(t=123)
  9. 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>() { !!... }
  10. Either<A, B> シンプルな構文例 val eiei = Right("123").map { it.toInt() }

    !// Right(b=123) val eiei2 = Left(IllegalArgumentException("OMG!")) !// Left(a=IllegalArgumentException)
  11. 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) }
  12. Either<A, B> If や fold での判定する場合は if (eiei.isLeft) { !!...

    } if (eiei.isRight) { !!... } eiei.fold({ e !-> !// if Left }, { !// if Right })
  13. 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>()
  14. Validated<E, A> If や fold での判定する場合は if (vali.isInvalid) { !!...

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

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

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

    bbb = Invalid("abcd").getOrElse { 0 } Validated#orElse, getOrElse で Invalid の場合などに違う値を
  18. 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) }) }
  19. 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
  20. 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>() {
  21. Try<A> シンプルな構文例 val aaa: Try<Int> = Try { 123 }

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

    if (trrr.isSuccess) { !!... } trrr.fold({ e !-> !// if Failure }, { !// if Success })
  23. 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
  24. 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)
  25. Try<A> Try#recover で代替の値を設定して Either 型として扱う val aaa: Try<Int> = Try

    { throw Throwable("OMG!") } val bbb = aaa.recover { e !-> 123 }.toEither()
  26. 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 型として扱うなど
  27. 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 { … }
  28. 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 の世界に持ち込む
  29. kotlinx.coroutines integration DeferredK { BitmapFactory.decodeFile(!!...) } .runAsync { result !->

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