Reputation: 4469
given val l = List( List(0), List(1) )
The for-loop :
for {
x <- l
_ = println(x)
y <- x
} {println(y)}
//would prints :
List(0)
List(1)
0
1
The prints are in the wrong order !!
Isn't it be translated into the following? the translation of for-loop :
l.foreach(
x => {
println(x)
x.foreach(y => println(y))
}
)
List(0)
0
List(1)
1
---
My Questions:
_ = print()
) in for-condition part? (just a print()
will not compile )Upvotes: 2
Views: 2263
Reputation: 24802
The problem here is your translation of the for loop (which mostly comes from the erronous translation of the _ = println(x)
).
From the Scala Specs, you can see (page 90) :
• A generator
p <- e
followed by a value definitionp' = e'
is translated to the following generator of pairs of values, wherex
andx'
are fresh names:
(p, p' ) <- for (x@p <- e) yield { val x'@p' = e' ; (x, x') }
Applying the transformations step by step, you get :
for {
(x, u) <- for (x <- l) yield {val u = println(x); (x, u)}
y <- x
} {println(y)}
for {
(x,u) <- l.map {case x => val u = println(x); (x,u)}
y <- x
} {println(y)}
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => for ( y <- x ) {println(y)} }
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => x.foreach {case y => println(y)} }
and
scala> :paste
// Entering paste mode (ctrl-D to finish)
l.map { case x => val u = println(x); (x,u) }
.foreach { case (x,u) => x.foreach {case y => println(y)} }
// Exiting paste mode, now interpreting.
List(0)
List(1)
0
1
Upvotes: 2
Reputation: 20950
The loop you gave gets translated to the following:
l.map(((x) => {
val x$1 = println(x);
scala.Tuple2(x, x$1)
})).foreach(((x$2) => x$2: @scala.unchecked match {
case scala.Tuple2((x @ _), (x$1 @ _)) => x.foreach(((y) => println(y)))
}))
You can find out things like this by quasiquoting them in an interpreter.
What happens is that the assignment is associated with the generator before it -- like if you wrote for (x <- l; h = x.head)
, where coupling these two is obiously necessary.
If you want the side effect to happen for each subsequent generator, you have to write the follwing:
for {
x <- l
y <- {println(x); x}
} {println(y)}
This results in your desired printout, and compiles quite to what you had expected:
l.foreach(((x) => {
println(x);
x
}.foreach(((y) => println(y)))))
As to why explicitly discarding arguments in generators this is necessary -- there are two issues. First, println
is in a different monad. Running expressions only for effect in monad comprehensions only make sense in the same monad, obviously; and that's also not really useful in the list monad. If you were working in a hypothetical IO monad, you would be able to do the following:
for {
y <- readLn()
_ <- printLn(x)
}
But here comes the second issue: Scala does not even here allow to discard unit results, we still have to pattern match on _
.
Don't ask me why, it's just in the standard. IMHO, it would actually make sense to allow this, as Haskell does. One reason I could think of, however, is just what you are trying to do: mixing side effects of one monad with another. They could have wanted to ban this once and for all. It could also have to do with the fact that rewriting of for comprehensions in Scala is based more on duck typing than it is in Haskell, has maybe more corner cases and even happens (as far as I know) before type checking, which would complicate such "mixing" a lot.
Upvotes: 3