What is a Monad in Scala?


#1

I am teaching myself Scala and trying to read about them but I cannot really figure it out.


#2

Monads are a category of data types. Informally, anything that has a type parameter, a constructor that takes an element of that type, and a flatMap method (which has to obey certain laws) is a monad.

There are many different approaches to explaining monads. One reason for that may be, that different monads have very different effects. You have probably used some monads already: Option, List, and Either are monads. Future is a monad (if you don’t look too closely). If you look at their APIs, you will notice, that they have similar methods, although they do different things.

The book Scala with Cats explains monads as

A monad is a mechanism for sequencing computations.

which is what methods like map and flatMap do. For example, with an Option, you can use them to chain several functions together, which do not need to handle an absent value. With a List, you chain operations together, that work on single elements.

A type that has a map method is called a Functor, it chains together functions that work on elements of the generic type of the Functor. A Monad, which has a flatMap, is more powerful, as flatMap allows for functions, that return an instance of the Monad, thereby changing the effect the monad has. For example, map on Option can only convert a present value to another present value, flatMap can also convert it to None. For lists, flatMap allows to change the length of the list (by returning a different number of elements from the function given to it).

I recommend reading the Scala with Cats chapter on monads, it is free to read online and I think it is well written.


#3

Well, that’s pretty formal, IMO. What is informal is calling a monad computational context or mechanism for sequencing computations.

I think it’s best to stick to formal definition, understand consquences of the laws ( https://wiki.haskell.org/Monad_laws ) and see examples of monads like:

  • collections like List, Option, Set, etc
  • abstractions over asynchronicity, side-effects, errors, etc like Future, Try, Task, IOMonad, etc
  • parsers (of any input e.g. XMLs, JSONs, source code, binary formats, etc) like FastParse from Li Hayoi
  • generators (of any output) like ScalaCheck or ScalaProps that are used for testing

#4

I gave a talk about monads at Scala Days last year that may or may not be helpful. It’s a very simple idea that’s hard to wrap your head around because it’s so general, and it will probably take some time to click. You’re not alone here.


#5

What has helped me is to think of a monad as a container with operations (map/flatMap/filter/foreach) that can act on the contents without extracting it first.

For instance, this function “opens” the option to get its content:

def display(msg: String, prompt: Option[String] = None) = {
  prompt match {
    case None => ()
    case Some(str) => print(str)
  }
  println(msg)
}

Contrast this with:

def display(msg: String, prompt: Option[String] = None) = {
  prompt foreach print
  println(msg)
}

where the option is told to print its content (if any).

I find it especially powerful with futures because in that case, there is no alternative: There is no way (short of using an additional thread) to “extract” the contents of a future that is still being computed asynchronously.

The beauty of Scala is the for/yield construct that results in code much easier to read than chained calls to map and flatMap:

val futureImage: Future[Image] = ...
val futureText: Future[Text] = ...
val futurePage: Future[Page] =
  for {
    image <- futureImage
    text <- futureText
  } yield makePage(image, text)

The Java equivalent is harder to read:

CompletionStage<Image> futureImage = ...
CompletionStage<Text> futureText = ...
CompletionStage<Page> futurePage =
  futureImage.thenCompose(image ->
    futureText.thenApply(text ->
      makePage(image, text)
    ));

(especially given than flatMap is called thenCompose and map is called thenApply on Java’s futures)