Skip to content

Instantly share code, notes, and snippets.

@caiorss
Forked from Y4suyuki/README.md
Created October 25, 2016 03:58
Show Gist options
  • Save caiorss/94ed5b0d5d7aff020b98f488d8c3d701 to your computer and use it in GitHub Desktop.
Save caiorss/94ed5b0d5d7aff020b98f488d8c3d701 to your computer and use it in GitHub Desktop.
Monad

Monad

What is Monad?

A monad M is a parametric type M[T] with two operations flatMap and unit that have to satisfy some laws.

trait M[T] {
  def flatMap[U](f: T => M[U]): M[U]
}

def unit[T](x: T): M[T]

Example of Monads

  • List is a monad with unit(x) = List(x)
  • Set is monad with unit(x) = Set(x)
  • Option is a monad with unit(x) = Some(x)
  • Generator is a monad with unit(x) = single(x)

flatMap is an operation on each of these types, whereas unit in Scala is different for each monad.

Monads and map

map can be defined for every monad as a combination of flatMap and unit:

m map f == m flatMap (x => unit(f(x)))
        == m flatMap (f andThen unit)

Monad Laws

To qualify as a monad, a type has to satisfy three law:

  • Associativity:
m flatMap f flatMap g == f flatMap (x => f(x) flatMap g)
  • Left unit:
unit(x) flatMap f == f(x)
  • Right unit:
m flatMap unit == m

Another type: Try

In the later parts of this course we will need a type named Try.

Try resembles Option, but instead of Some/None there is a Success case with a value and Failure case that contains an exception:

abstract class Try[+T]
case class Success[T](x: T)       extends Try[T]
case class Failure(ex: Exception) extends Try[Nothing]

Try is used to pass results of computations that can fail with an exception between threads and computers

Creating a Try

You can wrap up an arbitrary computation in a Try

Try(expr)  // gives Success(someValue) or Failure(someException)

Here's an implementation of Try:

object Try {
  def apply[T](expr: => T): Try[T] =
    try Success(expr)
    catch {
      case NonFatal(ex) => Failure(ex)
    }

Composing Try

Just like with Option, Try-valued computations can be composed in for expressions.

for {
  x <- computeX
  y <- computeY
} yield f(x, y)

If computeX and computeY succeed with result Success(x) and Success(y) this will return Success(f(x,y)) If either computation fails with an exception ex, this will return Failure(ex).

abstract class Try[T] {
  def flatMap[U](f: T => Try[U]): Try[U] = this match {
    case Success(x) => try f(x) catch { case NonFatal(ex) => Failure(ex) }
    case fail: Failure => fail
  }
  
  def map[U](f: T => U): Try[U] = this match {
    case Success(x) => Try(f(x))
    case fail: Failure => fail
  }}

So for a Try value t,

t map f == t flatMap (x => Try(f(x)))
        == t flatMap (f andThen Try)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment