Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is a for expression with multiple monads translated in scala?

Tags:

monads

scala

I am reading "Programming in Scala 2nd Edition" and I have some idea about monad from a Haskell course I took. However, I do not understand why the following code "magically" works:

scala> val a: Option[Int] = Some(100)
a: Option[Int] = Some(100)

scala> val b = List(1, 2, 3)
b: List[Int] = List(1, 2, 3)

for ( y <- b; x <- a ) yield x;
res5: List[Int] = List(100, 100, 100)

I do not understand the above because according to the book's Chapter 23.4, the for expression is translated to something like:

b flatMap ( y =>
  a map ( x => x )
)

I am puzzled why the above code compiles because y => a map (x => x) is of type Int => Option[Int], while the b.flatMap expects a Int => List[Something].

On the other hand, the following code does NOT compile (which is good otherwise I would be more lost):

scala> for ( x <- a; y <- b ) yield y;
<console>:10: error: type mismatch;
 found   : List[Int]
 required: Option[?]
              for ( x <- a; y <- b ) yield y;
                          ^

So what is magical with the first example?

like image 612
user716468 Avatar asked Feb 24 '13 03:02

user716468


1 Answers

[…] b.flatMap expects a Int => List[Something].

That is not true: what it expects is an Int => GenTraversableOnce[Something]. (See http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List, and search the page for flatMap.) List[A] is a subtype of GenTraversableOnce[A] by inheritance. A function of type Int => List[Something] can be substituded because of the covariance of the result R of Function1, which is defined as: trait Function1[-T1, +R].

Option[A] is not a GenTraversableOnce[A], but there is an implicit conversion in Option's companion object: implicit def option2Iterable[A](xo: Option[A]): Iterable[A]. Iterable[A] is a subtype of GenTraversableOnce[A]. So the for-expression will get expanded to

b flatMap ( y =>
  option2Iterable(a map ( x => x ))
)

On the other hand, the following code does NOT compile […]

This is because a.flatMap, by contrast, is more specific: it really does require an Int => Option[Something]. (See http://www.scala-lang.org/api/current/index.html#scala.Option, and search the page for flatMap.) This makes sense, since an Option[Something] can only hold one value, so you couldn't flatten an arbitrary GenTraversableOnce[Something] into it. The only thing that can successfully be flattened into an Option[Something] is another Option[Something].

like image 76
ruakh Avatar answered Nov 13 '22 14:11

ruakh