This is an old revision of the document!

6 - Monads

Monads, baby!
Today we look at certain kinds of computations from a truly functional perspective:

  • computations that might or might not have a value
  • computations that might fail
  • computations that will finish at some point

But we won't get into math and category theory for this.
We'll see what a monad is from a programming perspective, directly in the Scala language.

You can find today's contents here.


Let's implement some of our own monads.

  1. Let's write a monad equivalent with Option. Let's call this Maybe, as in Haskell.
    • create a trait Maybe[+A] (remember the [A] is equivalent to Java's <A> syntax for generics and ignore the + for now)
    • add some functional primitives as methods to Maybe:
      • the monad requirements: map, flatMap, filter
      • zipWith, which receives a Maybe[B] and returns a Maybe[(A,B)]
        The signature is just a tiny bit more complex:
        def zipWith[B](other: Maybe[B]): Maybe[(A,B)]

        If either this or other is empty, the result will be an empty Maybe.

      • exists, which receives a predicate (a function of A to Boolean) and returns true if this instance contains an element which satisfies the predicate
      • orElse, a special method for Maybe, which returns this instance if it's not empty, or an alternative otherwise; the signature will be
        def orElse[B >: A](alternative: Maybe[B]): Maybe[B]

        (ignore what you don't understand, for now, and come back to this exercise after lab 9)
        How can you delay the computation of the alternative until you actually need it? (hint: call by value/call by name)

    • add some utility methods to the Maybe trait:
      • isEmpty
      • toList
      • size
    • create two case subclasses: Just[A] and None and implement the methods
      • what is None, actually? a case class? a case object?
      • methods should be quick to write and probably take two lines of code, at most
  2. An equivalent to Scala's Try monad, call this Attempt[A].
    • as before, create a trait Attempt[A] and two case subclasses, Success[A] and Fail
    • Success has a member of type A, Fail has a member of type Throwable
    • add some functional primitives to your Attempt and implement them in the subclasses:
      • map, flatMap, filter
      • zipWith
      • exists
      • isEmpty, toList, size
      • a special method - recoverWith; the method returns an alternative Attempt computation if this one fails
  3. Talk about the Future[T] (pun intended) if you finish early!
sesiuni/scala/lab6.1467648493.txt.gz · Last modified: 2016/07/04 19:08 by dciocirlan