-->

What exactly makes Option a monad in Scala?

2020-02-07 18:02发布

问题:

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?

回答1:

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.



回答2:

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".



回答3:

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.