39

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?

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Incerteza
  • 32,326
  • 47
  • 154
  • 261

4 Answers4

63

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.

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
  • 3
    so Option doesn't know that it itself is a monad? Unlike Maybe in Haskell. – Incerteza Aug 18 '14 at 10:37
  • 2
    precisely, in Scala everything that has a flatmap is considered a "monad" and it can be used in for-comprehensions (the equivalent of a do-block in haskell) – Gabriele Petronella Aug 18 '14 at 10:38
  • `Technically speaking, it must also support a unit operation (return, in Haskell terms) to create a monad out of a bare value, which in case of Option is its constructor.` --> how does Option reflect the fact that its constructor is "unit"? – Incerteza Aug 18 '14 at 10:41
  • it does not. That's only true from a theoretical standpoint, but it's not enforced or known by the language. – Gabriele Petronella Aug 18 '14 at 10:42
  • 6
    "so Option doesn't know that it itself is a monad? Unlike Maybe in Haskell." In Haskell `Monad` is just a class. Simply having a type class defined for Monad is not enough to really be a monad. The required functions must be properly defined. If I create a typeclass Monad for my own type `Foo` but screw up the implementation of `bind` for example, `Foo` is not a monad. It is perfectly possible to create the equivalent of the Monad class in Scala - I think it is actually done in Scalaz. – vptheron Aug 18 '14 at 12:31
  • How does Scalaz implement unit without return-type polymorphism? – J. Abrahamson Aug 18 '14 at 12:53
  • @AlexanderSupertramp Scala doesn't enforce the existence of unit or the monad laws, and Haskell doesn't enforce the monad laws. In both languages it's therefore possible to have a non-monad treated as a monad, even if Scala's monads can be further from the definition. – AndrewC Aug 18 '14 at 13:21
  • In other words, a typeclass can enforce an interface, but it can't assure the correctness of the implementation. – Gabriele Petronella Aug 18 '14 at 13:25
  • 1
    @J.Abrahamson by emulating typeclasses using implicit parameters. Similar to this: https://gist.github.com/igstan/66e9bfd31d6ff03b233d. – Ionuț G. Stan Aug 18 '14 at 14:28
  • Ah, nice! Can you write a Monad-generic value like Haskell's `forall m a . Monad m => m a`? – J. Abrahamson Aug 18 '14 at 14:30
  • 1
    @J.Abrahamson my Haskell is a bit rusty, so I'm not sure if I got the quantifiers right, but I think this is the equivalent: `def quux[M[_], A](implicit m: Monad[M]): M[A]`. – Ionuț G. Stan Aug 18 '14 at 14:35
  • @J.Abrahamson yes you can, since scala supports higher-order kinds, so you can talk about `M[_]` and write generic methods that work on type constructors. – Gabriele Petronella Aug 18 '14 at 14:53
  • To respect the monad laws, shouldn't the `unit` operation be `Some()`? The `Option` constructor turns `null` into `None`, but `flatMap` doesn't reverse that. – Dan Getz Aug 18 '14 at 18:23
13

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

Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • 1
    but Option doesn't implement FilterMonadic. What do you mean by duck-typing in Scala? Duck-typing is about dynamically-typed languages. – Incerteza Aug 18 '14 at 10:44
  • 4
    It means that the compiler treats a for comprehension as a sort of macro and replaces it with calls to `flatMap`, `map`, `foreach` and/or `withFilter`. The typechecking is performed on this translation, not on the for comprehension. This is why I'm saying that it's duck-typing. Anything that looks like `FilterMonadic` will be accepted by a for comprehension. – Ionuț G. Stan Aug 18 '14 at 10:48
  • 1
    Another formulation might be that for comprehensions are untyped. That is, before expansion. – Ionuț G. Stan Aug 18 '14 at 11:03
9

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.

Luis Casillas
  • 29,802
  • 7
  • 49
  • 102
  • 3
    Actually, there *can't* be a `Monad` type in Java, since `Monad` is higher-kinded, but Java doesn't have higher-kinded parametric polymorphism. – Jörg W Mittag Aug 18 '14 at 18:16
  • @JörgWMittag: correct. See e.g. [this earlier answer of mine to another question](http://stackoverflow.com/questions/21170493/when-are-higher-kinded-types-useful/21174565#21174565). – Luis Casillas Aug 18 '14 at 19:10
  • Another reason that Optional shouldn't be considered as a true monad - if you read the Java 8 docs carefully, they state that Optional should only be used as the return type of a method, not as the type of any piece of state. This is actually a fairly significant shortcoming, and unfortunately there's no obvious way to fix it. – kittylyst Oct 23 '16 at 07:45
0

Scala, per se, does not provide the notion of a monad. You can express a monad as a typeclass but Scala also doesn't provide the notion of a typeclass. But Cats does. So you can create a Monad in Scala with the necessary boiler plate, e.g. traits and implicits cleverly used, or you can use cats which provides a monad trait out of the box. As a comparison, Haskel provides monads as part of the language. Regarding your specific question, an Option can be represented as a monad because it has a flatMap method and a unit method (wrapping a value in a Some or a Future, for example).