Fight the Future



One Div Zero: Monads are Elephants Part 3の翻訳続き。

Functors and Monads, Alive, Alive Oh(ファンクターとモナドはまだまだ続く)

As you may have guessed by now all monads are functors so they must follow the functor laws. In fact, the functor laws can be deduced from the monad laws. It's just that the functor laws are so simple that it's easier to get a handle on them and see why they should be true.


As a reminder, a Scala monad has both map and flatMap methods with the following signatures


class M[A] {
 def map[B](f: A => B):M[B] = ...
 def flatMap[B](f: A=> M[B]): M[B] = ...

Additionally, the laws I present here will be based on "unit." "unit" stands for a single argument constructor or factory with the following signature


def unit[A](x:A):M[A] = ...

"unit" shouldn't be taken as the literal name of a function or method unless you want it to be. Scala doesn't specify or use it but it's an important part of monads.


Any function that satisfies this signature and behaves according to the monad laws will do. Normally it's handy to create a monad M as a case class or with a companion object with an appropriate apply(x:A):M[A] method so that the expression M(x) behaves as unit(x).


The Functor/Monad Connection Law: The Zeroth Law(ファンクター/モナド結合法則:0番目の法則)

In the very first installment of this series I introduced a relationship


  • FM1. m map f ≡ m flatMap {x => unit(f(x))}

This law doesn't do much for us alone, but it does create a connection between three concepts: unit, map, and flatMap.


This law can be expressed using "for" notation pretty nicely


  • FM1a. for (x <- m) yield f(x) ≡ for (x <- m; y <- unit(f(x))) yield y

Flatten Revisited(flattenふたたび)

In the very first article I mentioned the concept of "flatten" or "join" as something that converts a monad of type M[M[A]] into M[A], but didn't describe it formally. In that article I said that flatMap is a map followed by a flatten.


  • FL1. m flatMap f ≡ flatten(m map f)

This leads to a very simple definition of flatten


  • flatten(m map identity) ≡ m flatMap identity // substitute identity for f
  • FL1a. flatten(m) ≡ m flatMap identity // by F1

So flattening m is the same as flatMapping m with the identity function. I won't use the flatten laws in this article as flatten isn't required by Scala but it's a nice concept to keep in your back pocket when flatMap seems too abstract.