What exactly makes Option a monad in Scala?
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 ofOption
is theSome
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.
Anything that (partially) implements, through duck-typing, the FilterMonadic
trait is considered to be a monad in Scala. This is different than how monads are represented in Haskell, or the Monad
typeclass in scalaz. However, in order to benefit of the for
comprehension syntactic sugar in Scala, an object has to expose some of the methods defined in the FilterMonadic
trait.
Also, in Scala, the equivalent of the Haskell return
function is the yield
keyword used for producing values out of a for
comprehension. The desugaring of yield
is a call to the map
method of the "monad".
The way I'd put it is that there's an emerging distinction between monads as a design pattern vs. a first-class abstraction. Haskell has the latter, in the form of the Monad
type class. But if you have a type that has (or can implement) the monadic operations and obeys the laws, that's a monad as well.
These days you can see monads as a design pattern in Java 8's libraries. The Optional
and Stream
types in Java 8 come with a static of
method that corresponds to Haskell return
, and a flatMap
method. There is however no Monad
type.
Somewhere in between you also have the "duck-typed" approach, as Ionuț G. Stan's answer calls out. C# has this as well—LINQ syntax isn't tied to a specific type, but rather it can be used with any class that implements certain methods.