Calling monadic bind flatMap
may cause some confusion. flatMap
is very list (or container) specialised name which gives false intuition. Even worse is to call monadic join flatten
. Monadic join isn't very good name either, if you drop monadic.
OTOH: flatten
does flatten type structure, F[F[A]] => f[A]
, but I'm not sure how many of us think on the type-level first.
I'm very glad that in scalaz monadic bind is bind
. Unfortunately Scala's for
comprehension desugars into flatMap
's, AFAIK.
One (a bit contrived) example: https://github.com/phadej/playground/blob/master/scala/src/main/scala/homogenicpair.scala
import homogenicpair.implicits._
val a: Tuple2[Int, Int] = (2, 3)
def f(x: Int): Tuple2[Int, Int] = (x, x * x)
val x = a flatMap f
// Exiting paste mode, now interpreting.
a: (Int, Int) = (2,3)
f: (x: Int)(Int, Int)
x: (Int, Int) = (2,9)
The flatMap
I defined for Tuple2[A, A]
obeys monad laws.
Assume (Scala) language evolved enough to support type level natural numbers (actually it is). Then we could have
trait Vec[N <: Nat, A]
So Vec[Zero, _]
is isomorpic to Unit
, Vec[Succ[Zero], A]
is Id[A]
Vec[_2, A]
is Tuple2[A,A]
and so on.
And it's possible to give monadic structure to Vec
for every N
. The ZipList
structure:
// scalaz has a bit different definitions
def pure[A](x: A): Vec[N, A] = repeat[N](x)
def ap[A,B](f: Vec[N. A => B], x: Vec[N, A]): Vec[N, B] = zipWith[N]((ff, xx) => ff(xx), f, x)
def bind[A,B](f: Vec[N, A => Vec[N, B]], x: Vec[N, A]): Vec[N, B] = diag[N](x map f)
def diag[A](f: Vec[N, Vec[N, A]]): Vec[N, A] = ??? /* take elements on diagonal of N × N matrix */
//
Then if we take Vec[_2, Int]
≡ pair of Int
's:
val a: Tuple2[Int, Int] = (1, 2)
def f(x: Int): Tuple2[Int, Int] = Tuple2(x, x * x)
val x: Tuple2[Int, Int] = a flatMap f // what is the result?
// it's Tuple2(1, 4)