Why does a Scala for-comprehension have to start with a generator?

haskell, monads, scala

Solution

`for` comprehensions do not catch exceptions. `Try` does, and it has the appropriate methods to participate in for-comprehensions, so you can

for {
  manager <- Try { Manager.getManager() }
  ...
}

But then it's expecting `Try` all the way down unless you manually or implicitly have a way to switch container types (e.g. something that converts `Try` to a `List`).

So I'm not sure your premises are right. Any assignment you made in a for-comprehension can just be made early.

(Also, there is no point doing an assignment inside a for comprehension just to yield that exact value. Just do the computation in the yield block.)

(Also, just to illustrate that multiple types can play a role in `for` comprehensions so there's not a super-obvious correct answer for how to wrap an early assignment in terms of later types:

// List and Option, via implicit conversion
for {i <- List(1,2,3); j <- Option(i).filter(_ <2)} yield j

// Custom compatible types with map/flatMap
// Use :paste in the REPL to define A and B together
class A[X] { def flatMap[Y](f: X => B[Y]): A[Y] = new A[Y] }
class B[X](x: X) { def map[Y](f: X => Y): B[Y] = new B(f(x)) }
for{ i <- (new A[Int]); j <- (new B(i)) } yield j.toString

Even if you take the first type you still have the problem of whether there is a unique "bind" (way to wrap) and whether to doubly-wrap things that are already the correct type. There could be rules for all these things, but for-comprehensions are already hard enough to learn, no?)

Problem

According to the Scala Language Specification (§6.19), "An enumerator sequence always starts with a generator". Why? I sometimes find this restriction to be a hindrance when using `for`-comprehensions with monads, because it means you can't do things like this: ``` def getFooValue(): Future[Int] = { for { manager = Manager.getManager() // could throw an exception foo <- manager.makeFoo() // method call returns a Future value = foo.getValue() } yield value } ``` Indeed, `scalac` rejects this with the error message `'<-' expected but '=' found`. If this was valid syntax in Scala, one advantage would be that any exception thrown by `Manager.getManager()` would be caught by the `Future` monad used within the `for`-comprehension, and would cause it to yield a failed `Future`, which is what I want. The workaround of moving the call to `Manager.getManager()` outside the `for`-comprehension doesn't have this advantage: ``` def getFooValue(): Future[Int] = { val manager = Manager.getManager() for { foo <- manager.makeFoo() value = foo.getValue() } yield value } ``` In this case, an exception thrown by `foo.getValue()` will yield a failed `Future` (which is what I want), but an exception thrown by `Manager.getManager()` will be thrown back to the caller of `getFooValue()` (which is not what I want). Other possible ways of handling the exception are more verbose. I find this restriction especially puzzling because in Haskell's otherwise similar `do` notation, there is no requirement that a `do` block should begin with a statement containing `<-`. Can anyone explain this difference between Scala and Haskell? Here's a complete working example showing how exceptions are caught by the `Future` monad in `for`-comprehensions: ``` import scala.concurrent._ import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global import scala.util.{Try, Success, Failure} class Foo(val value: Int) { def getValue(crash: Boolean): Int = { if (crash) { throw new Exception("failed to get value") } else { value } } } class Manager { def makeFoo(crash: Boolean): Future[Foo] = { if (crash) { throw new Exception("failed to make Foo") } else { Future(new Foo(10)) } } } object Manager { def getManager(crash: Boolean): Manager = { if (crash) { throw new Exception("failed to get manager") } else { new Manager() } } } object Main extends App { def getFooValue(crashGetManager: Boolean, crashMakeFoo: Boolean, crashGetValue: Boolean): Future[Int] = { for { manager <- Future(Manager.getManager(crashGetManager)) foo <- manager.makeFoo(crashMakeFoo) value = foo.getValue(crashGetValue) } yield value } def waitForValue(future: Future[Int]): Unit = { val result = Try(Await.result(future, Duration("10 seconds"))) result match { case Success(value) => println(s"Got value: $value") case Failure(e) => println(s"Got error: $e") } } val future1 = getFooValue(false, false, false) waitForValue(future1) val future2 = getFooValue(true, false, false) waitForValue(future2) val future3 = getFooValue(false, true, false) waitForValue(future3) val future4 = getFooValue(false, false, true) waitForValue(future4) } ``` Here's the output: ``` Got value: 10 Got error: java.lang.Exception: failed to get manager Got error: java.lang.Exception: failed to make Foo Got error: java.lang.Exception: failed to get value ``` This is a trivial example, but I'm working on a project in which we have a lot of non-trivial code that depends on this behaviour. As far as I understand, this is one of the main advantages of using `Future` (or `Try`) as a monad. What I find strange is that I have to write ``` manager <- Future(Manager.getManager(crashGetManager)) ``` instead of ``` manager = Manager.getManager(crashGetManager) ``` (Edited to reflect @RexKerr's point that the monad is doing the work of catching the exceptions.)

Original source