Monad

In category theory, a monad builds on a certain morphism between categories. In functional programming, this allows a function of type A => F[B] to be lifted to a function of type F[A] => F[B] for some type constructor F.

.--------------.         .-------------------.
|  Category _  |         |   Category F[_]   |
|--------------|         |-------------------|
| A  ~~~~~~~~~~~ pure ~~~~~> F[A]            |
| B  ~~~~~~~~~~~ pure ~~~~~> F[B]            |
| A  ======================> F[B]            |
|           |  |         |                   |
|           '~~~ flatMap ~~> F[A] => F[B]    |
'--------------'         '-------------------'

This diagram can be read as:

  1. The category _ contains objects of type A and B, and a morphism from A to F[B], which is in the category F[_]
  2. The category F[_] contains objects of type F[A] and F[B], and a morphism from F[A] to F[B]
  3. A monad for F[_] converts the A object into the F[A] object via pure
  4. A monad for F[_] converts the A => F[B] morphism into the F[A] => F[B] morphism via flatMap
trait Monad[M[_]] extends Applicative[M] {
  def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B]
  def join[A](mma: M[M[A]]): M[A] =
    flatMap(mma)(ma => ma)
  def ap[A, B](f: M[A => B])(ma: M[A]): M[B] =
    flatMap(f)({ g => flatMap(ma)({ a => pure(g(a)) }) })
}

object Monad {
  import Functor._
  implicit class MonadOps[A, M[_]: Monad](ma: M[A]) extends FunctorOps[A, M](ma) {
    def flatMap[B](f: A => M[B]): M[B] =
      implicitly[Monad[M]].flatMap(ma)(f)
    def >>=[B](f: A => M[B]): M[B] = flatMap(f)
  }
}