What exactly makes Option a monad in Scala?

アレックス picture アレックス · Aug 18, 2014 · Viewed 8.8k times · Source

I know what the monads are and how to use them. What I don't understand is what makes, let's say, Option a monad?

In Haskell a monad Maybe is a monad because it's instantiated from Monad class (which has at least 2 necessary functions return and bind that makes class Monad, indeed, a monad).

But in Scala we've got this:

sealed abstract class Option[+A] extends Product with Serializable { ... }
trait Product extends Any with Equals { ... }

Nothing related to a monad.

If I create my own class in Scala, will it be a monad by default? Why not?

Answer

Gabriele Petronella picture Gabriele Petronella · Aug 18, 2014

Monad is a concept, an abstract interface if you will, that simply defines a way of composing data.

Option supports composition via flatMap, and that's pretty much everything that is needed to wear the "monad badge".

From a theoretical point of view, it should also:

  • support a unit operation (return, in Haskell terms) to create a monad out of a bare value, which in case of Option is the Some constructor
  • respect the monadic laws

but this is not strictly enforced by Scala.

Monads in scala are a much looser concept that in Haskell, and the approach is more practical. The only thing monads are relevant for, from a language perspective, is the ability of being used in a for-comprehension.

flatMap is a basic requirement, and you can optionally provide map, withFilter and foreach.

However, there's no such thing as strict conformance to a Monad typeclass, like in Haskell.

Here's an example: let's define our own monad.

class MyMonad[A](value: A) {
  def map[B](f: A => B) = new MyMonad(f(value))
  def flatMap[B](f: A => MyMonad[B]) = f(value)
  override def toString = value.toString
}

As you see, we're only implementing map and flatMap (well, and toString as a commodity). Congratulations, we have a monad! Let's try it out:

scala> for {
  a <- new MyMonad(2)
  b <- new MyMonad(3)
} yield a + b
// res1: MyMonad[Int] = 5

Nice! We are not doing any filtering, so we don't need to implement withFilter. Also since we're yielding a value, we don't need foreach either. Basically you implement whatever you wish to support, without strict requirements. If you try to filter in a for-comprehension and you haven't implemented withFilter, you'll simply get a compile-time error.