Search code examples
scalafor-comprehension

What does it mean that the types have to align in for-comprensions?


Can someone please elaborate what it means when the types have to align with scala for comprehensions?

for {
..
..
}

If the calls all return Futures then it will be fine? Just trying to understand when it works and when it doesn't work.


Solution

  • For-comprehension desugars to map/flatMap calls so consider their signatures. For example, consider Option#flatMap

    def flatMap[B](f: (A) => Option[B]): Option[B]
    

    We see Options continue to be involved. Now observe since

    for {
      a <- Some(41)
      b <- Some(1)
    } yield a + b
    

    becomes

    Some(41).flatMap(a => Some(1).map(b => a + b))
    

    that means if you tried to mix monadic types inside for-comprehension such as

    for {
      a <- Some(41)
      b <- Try(1)
    } yield a + b
    

    then it would desugar to

    Some(41).flatMap(a => Try(1).map(b => a + b))
                           |
                      types do not align
    

    but we already seen that Option#flatMap expects A => Option[B] not A => Try[B].

    One place where it seems as if you can break that rule is when mixing Option with List

    scala> for {
         |   a <- List(41)
         |   b <- Some(1)
         | } yield (a + b)
    val res0: List[Int] = List(42)
    

    but this works because List#flatMap takes a function from A to IterableOnce and Option has been made IterableOnce in Scala 2.13

    def flatMap[B](f: A => IterableOnce[B])
    

    Note the other way around will not work though:

    scala> for {
         |   a <- Some(41)
         |   b <- List(1)
         | } yield a + b
             b <- List(1)
               ^
    On line 3: error: type mismatch;
            found   : List[Int]
            required: Option[?]
    

    In general given an effectful type F[A] then inside for-comprehension F cannot vary unless we are using a subtype, on the other hand A can indeed vary.