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

FP with Kotlin/Arrow: Monad Comprehensions & Parallel Processing

Matt Moore
February 11, 2020

FP with Kotlin/Arrow: Monad Comprehensions & Parallel Processing

Monads make our code safer, provide better guarantees with error handling, and clean our code up to be easier to read. In this talk we’ll take a look at Arrow’s IO monad, and how we can combine it with Either.

When composing multiple monads you’ll start to notice that you end up indenting a lot of code as you begin to nest flatMaps into flatMaps with multiple monads for each step in the happy path. We’ll take a look at another strategy for handling monadic composition in Arrow with monad comprehensions.

Monad comprehensions give us the flexibility to make our IO operations a little easier to navigate by writing each sequential operation line by line, making our code look more imperative, while still executing in a functional manner. And we still get to keep all the safety guarantees that monads provide in the process. We’ll also see how monad comprehensions were inspired by Haskell’s do notation and Scala’s for comprehensions.

Next, we’ll examine how to handle parallel processing the functional way with parallel map strategies combined with monads, allowing us to do parallel processing with far less code and safer guarantees.

Matt Moore

February 11, 2020
Tweet

More Decks by Matt Moore

Other Decks in Programming

Transcript

  1. The Problem We have data stored in APIs that we

    need to access: • Customers • Orders • Addresses
  2. The Problem Given an order number, I need to get

    a customer's address. Sequence of looking up an address: 1. Look up the order record for a given order number. 2. Find the customer associated with that order. 3. Get the customer's mailing address.
  3. The Problem If any of the lookups fail, we should

    return a message "Not found." • If the order number isn't valid, no sense in running a lookup on customer. • If customer can't be found, no sense searching for address.
  4. One Way Without Monads Will throw an exception if anything

    fails. We can add try/catch. We cannot parallelize this as easily as with monads (we'll get to that in a bit).
  5. Side Note: Return Type Inference My personal preference: include return

    type. The whole point of type systems is compiler checks for our code. If you omit them, you're bypassing the type check system. Kotlin, Scala and Haskell let you do type inference, but specifying it ensures compiler checks.
  6. Monad Composition: Sequential Processing If any of the monads in

    the sequence fails, the entire composition returns nothing. Nothing in nothing out. It won't throw an exception, so no accidental blow-ups if we forget to handle an error along the way. Each monad depends on the successful completion of the one before it.
  7. Executing the monad If the address is found, it will

    print. But it will print "Address lookup failed." if the address isn't found.
  8. Executing the monad To print "Not found." we use attempt()

    to convert from IO to Either<Left, Right> and map over the result with Kotlin's when expression: Either.Left: Composition failed. Either.Right: Composition succeeded. Found the address.
  9. Monadic composition guarantees comprehensive error handling. But IO monad (sequence

    in the program) requires nesting with flatMap. This gets difficult to read over time. There's a better way to do monadic composition.
  10. Monad Comprehension Remove Nesting Hell • A different way to

    handle monadic composition • Removes flatMap nesting • Gives more flexibility to parallelization of monads
  11. Yes, it looks imperative. But we've kept the same error-handling

    guarantees of monads. We've eliminated nesting. Monadic steps in our sequence go on separate lines.
  12. The Problem We have a text document partitioned across multiple

    files. We are given a list of files in a specific order that need to be read. They can be read in any order. However, we must write the final output in the order of the files we were given.
  13. With what we've learned We can get all the files

    in order with monad comprehensions. But this happens sequentially. If each file takes 2 seconds, 4 files take 8 seconds total. We can parallelize each file. Then assemble in order.
  14. Error messages Notice: Either.Left -> println(it.a.message) If "part3.txt" is missing,

    we get: data/part3.txt (No such file or directory) An exception isn't thrown (program won't blow up). This is good! Instead, the error message returns via algebraic data type Either.Left.
  15. Matt Moore Blog: Lambda Show Podcast: (Tech Industry Interviews +

    Tutorials) GitHub: Twitter: Email: Practical Functional Programming: (Kotlinlang Slack) https://mattmoore.io https://lambda.show https://github.com/mattmoore https://twitter.com/@mattmoore_io [email protected] #practical-functional-programming