@Russ @bjornregnell hi, yes, I agree, that’s why my answer to the question of what flatMap and flatten are was the following, aimed at illustrating their behaviour with as simple an example as I could think of:
scala> assert( Some(Some(3)).flatten == Some(3) )
scala> assert( "3".toIntOption == Some(3) )
scala> assert( Some("3").map(_.toIntOption) == Some(Some(3)) )
scala> assert( Some("3").map(_.toIntOption).flatten == Some(3) )
scala> assert( Some("3").flatMap(_.toIntOption) == Some(3) )
I like the example with Option because Option is very simple. Seeing an example with List (or Vector) is also important to begin to grasp the idea that each monad is very different and while they all have a flatMap function, it is so different in each monad, it is where the smarts/dna/uniqueness of each monad lives.
I found it very useful, early on, to compare the implementation of the flatMap function of different monads, e.g. Option and List below:
trait Functor[F[_]] {
def map[A,B](ma: F[A])(f: A => B): F[B]
}
trait Monad[F[_]] extends Functor[F] {
def unit[A](a: => A): F[A]
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
def map[A,B](ma: F[A])(f: A => B): F[B] =
flatMap(ma)(a => unit(f(a)))
def flatten[A](ma: F[F[A]]): F[A] =
flatMap(ma)(identity)
}
val optionMonad = new Monad[Option] {
def unit[A](a: => A): Option[A] = Some(a)
def flatMap[A,B](ma: Option[A])(f: A => Option[B]): Option[B] = ma match {
case Some(a) => f(a)
case None => None
}
}
val listMonad = new Monad[List] {
def unit[A](a: => A): List[A] = List(a)
def flatMap[A,B](ma: List[A])(f: A => List[B]): List[B] = ma match {
case Nil => Nil
case head::tail => f(head) ::: flatMap(tail)(f)
}
}
assert( optionMonad.map(Some(3))(_ + 1) == Some(4) )
assert( listMonad.map(List(1,2,3))(_ + 1) == List(2,3,4) )
assert( optionMonad.flatMap(Some("3"))(_.toIntOption) == Some(3) )
assert( listMonad.flatMap(List(1,0,4)){case 0 => List[Int]() case x => List(x,x+1,x+2)} == List(1,2,3,4,5,6) )
assert( optionMonad.flatten(Some(Some(3))) == Some(3) )
assert( listMonad.flatten(List(List(1), List(2,3), List(4,5,6))) == List(1,2,3,4,5,6) )