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

Arrow Fx - Functional Programming for the masses

Arrow Fx - Functional Programming for the masses

One of the big cons of Functional Programming can be the learning curve. In this talk, we’ll learn how to encode “effectful” programs in a controlled way following the FP principles through a direct syntax. You’ll think you’re writing imperative code! Some additional bits we’ll learn:

- What’s an effect
- Why we’d want to keep it under control
- How to control errors and encode deferred execution through IO and further more powerful data types to be released.
- How to use the power of Kotlin suspension system to enforce us to keep our side effects under control.
- And much more.

47 Degrees Academy

February 04, 2022
Tweet

More Decks by 47 Degrees Academy

Other Decks in Programming

Transcript

  1. Some valid features ✅ high order functions (functions as values)

    . val for immutability (properties). Algebraic data types. Sum & Product types (sealed class + data class). Enhances determinism (expressions & exhaustive evaluation). 
  2. Is it enough? Nope . It still lacks some important

    features: type classes, higher kinded types... Something on top of was needed! 
  3. Majority of programs contain uncontrolled "effects" . (Query data bases,

    perform network requests, print to console, render UI...) They escape program's control (can fail, be slow, mutate program's state, race conditions...) 
  4. Solution? We want program's logic to avoid side effects at

    all cost, so it can stay deterministic. But, can we avoid them at all? 
  5. Solution ✅ Push side effects to the "edge of the

    world" (program's entry point). Android Application or Activity Rest API controllers other programs main() 
  6. How? Depend on abstractions. (Same than OOP! ) Provide implementation

    details when running it. Effects are implementation details. 
  7. Let's step back Our program is composed by functions. A

    function without side effects is called "pure" ✨. 
  8. What is it? A function that is deterministic. Given the

    same inputs produces the same output without observable world changing effects. 
  9. Is this pure or impure? Target platform: JVM Running on

    kotlin v. 1.3.31 fun printHelloWorld() Unit = println("Hello World!") val result = printHelloWorld() 
  10. How to make it pure? Defer execution (return a function)

    Target platform: JVM Running on kotlin v. 1.3.31 fun printHelloWorld() = { println("Hello World!") } val computation = printHelloWorld() 
  11. How to make it pure? Alternative: Wrap it into the

    functional type IO<A>. Defers execution. Fits well in Functional codebases. 
  12. Is this pure or impure? Target platform: JVM Running on

    kotlin v. 1.3.31 fun printHelloWorld() IO<Unit> = IO { println("Hello World!") } val result = printHelloWorld() 
  13. Pure! The thunk is still not run, the computation stays

    pure until you explicitly run it. 
  14. Is this pure or impure? Target platform: JVM Running on

    kotlin v. 1.3.31 suspend fun printHelloWorld() Unit = println("Hello World!") val result = printHelloWorld() 
  15. Why? Suspended functions cannot run outside of an already suspended

    context. It's unsafe to call them in "the environment" . Kotlin compiler disallows you to run side effects in unsafe places. 
  16. @RestrictSuspension suspend fun printOuterHelloWorld() Unit = println(helloWorld()) suspend fun Restricted.printInnerHelloWorld()

    Unit = println(helloWorld()) @RestrictsSuspension class Restricted { suspend fun y() Unit = printOuterHelloWorld() fails to c suspend fun x() Unit = printInnerHelloWorld() works! } 
  17. ΛRROW Fx Pure functional effects on top of ΛRROW and

    Kotlin Coroutines System. Leverages direct syntax to write pure functional programs. 
  18. Fx DSL 4 primitives to write any possible program. fx

    {}, effect{}, bind(), unsafe {}. 
  19. fx {} Represents a deferred program that can contain effects.

    Target platform: JVM Running on kotlin v. 1.3.31 suspend fun printHello() Unit = println("Hello world") fun program() IO<Unit> = fx { printHello() } 
  20. effect {} Accepts an effect in the program. Wraps the

    side effects. Target platform: JVM Running on kotlin v. 1.3.31 suspend fun printHello() Unit = println("Hello world") fun program() = fx { effect { printHello() } } 
  21. ! (or .bind()) Resolves the effect. Equivalent to flatMap(). Target

    platform: JVM Running on kotlin v. 1.3.31 suspend fun printHello() Unit = println("Hello World") fun program() IO<Unit> = fx { !effect { printHello() } } fun main() { println(program()) } 
  22. bind() removes nesting - service1().flatMap { result1 - service2(result1).flatMap {

    result2 - service3(result2).map { result3 - Result(result3) - } - } - } + val result1 = !service1() + val result2 = !service2(result1) + val result3 = !service3(result2) + Result(result3) 
  23. unsafe {} Perform effects at the edge of the world.

    Target platform: JVM Running on kotlin v. 1.3.31 fun program() IO<Unit> = fx { !effect { println("Hello World") } } fun main() { edge of the world unsafe { runBlocking { program() } } } 
  24. Polymorphism Make your program parametric to any F. Target platform:

    JVM Running on kotlin v. 1.3.31 fun <F> Fx<F>.program() Kind<F, Unit> = fx { !effect { println("HelloWorld") } } fun main() { val fx = IO.fx() IO<A> as F unsafe { runBlocking { fx.program() } } } 
  25. Asynchronous fibers: Easy to spawn and manage Target platform: JVM

    Running on kotlin v. 1.3.31 fx { val op = effect { Thread.currentThread().name } val fiber = !NonBlocking.startFiber(op) val threadName: String = !fiber.join() !effect { println(threadName) } } 
  26. Asynchronous fibers: Recovering from async errors Target platform: JVM Running

    on kotlin v. 1.3.31 fx { val op = effect { throw RuntimeException("BOOM!") } val fiber = !NonBlocking.startFiber(op) val recovery = { error: Throwable effect { println("recovering from async exception" } !fiber.join().handleErrorWith(recovery) } 
  27. Concurrency: Races Target platform: JVM Running on kotlin v. 1.3.31

    fx { val op1 = effect { Thread.currentThread().name } val op2 = effect { Thread.currentThread().name } val op3 = effect { Thread.currentThread().name } val result = !NonBlocking.raceN(op1, op2, op3) !effect { println(result) } } 
  28. Concurrency: Direct style concurrent non blocking map Target platform: JVM

    Running on kotlin v. 1.3.31 fx { val (a, b, c) = !NonBlocking.parMapN( effect { Thread.currentThread().name }, effect { Thread.currentThread().name }, effect { Thread.currentThread().name }, Tuple3 ) !effect { println(listOf(a, b, c)) } } 
  29. Concurrency: Error handling Target platform: JVM Running on kotlin v.

    1.3.31 fx { val res = !NonBlocking.parMapN( effect { Thread.currentThread().name }, effect { throw RuntimeException("BOOM!") }, effect { Thread.currentThread().name }, Tuple3 ).handleErrorWith { error: Throwable effect { println("One of the ops failed!") } } !effect { println(res) } } 
  30. Switching execution contexts Target platform: JVM Running on kotlin v.

    1.3.31 val program = fx { continueOn(NonBlocking) val t1 = !effect { Thread.currentThread().name } continueOn(BlockingIO) val t2 = !effect { Thread.currentThread().name } continueOn(UI) val t3 = !effect { Thread.currentThread().name } !effect { println("$t1 $t2 $t3") } } 
  31. Target platform: JVM Running on kotlin v. 1.3.31 val program

    = fx { val acquire = effect { File("data.json").open() } val use = { file: File effect { println(file.toString() val release = { file: File, exitCase: ExitCase effect { when (exitCase) { is ExitCase.Completed println("complete is ExitCase.Canceled println("canceled" is ExitCase.Error println("error") } file.close() println("File closed") } } !acquire.bracketCase(release, use) } 
  32. Built in Cancellation Target platform: JVM Running on kotlin v.

    1.3.31 fun canceler() = fx { val (_, cancel) = !NonBlocking.startFiber(infiniteLoop()) !effect { delay(100) } !effect { println("main: I'm tired of waiting!") } !cancel !effect { println("main: Now I can quit.") } } 
  33. Abstracts over function arity Target platform: JVM Running on kotlin

    v. 1.3.31 val program = fx { val op1 = effect { "first" } val op2 = effect { 2 } val race = !NonBlocking.raceN(op1, op2) val winner = race.fold({ "first one won" }, { "second one !effect { println(winner) } } 
  34. Fx for all Monads Fx over Option Target platform: JVM

    Running on kotlin v. 1.3.31 fx { val one = !Option(1) val two = !Option(one + one) two } 
  35. Alternative ! styles (_) destructuring syntax Target platform: JVM Running

    on kotlin v. 1.3.31 fx { val (one) = Option(1) val (two) = Option(one + one) two } 
  36. Alternative ! styles explicit bind() Target platform: JVM Running on

    kotlin v. 1.3.31 fx { val one = Option(1).bind() val two = Option(one + one).bind() two } 
  37. KEEP-87 KEEP is already submitted ✅ . We're gathering feedback

    and will iterate further after JetBrains review. 
  38. Upcoming libraries Helios: Hyper fast serialization library (optics based! awesome

    syntax) ⚡ . Kollect: Efficient data loading from heterogeneous sources of data . Both are FP oriented, fit well in any programs using Kotlin + FP. More to come!: Streams, Android... 
  39. Where's Arrow? Already available in Maven Central and JCenter. Aiming

    for a first stable release around the end of 2019 . 
  40. Join us! Github Slack Gitter We are beginner friendly and

    provide 1:1 mentoring for both users & new contributors! +110 Contributors and growing! https://github.com/arrow-kt https://kotlinlang.slack.com/messages/C5UPMM0A0 https://gitter.im/arrow-kt/Lobby 