Scala "<-" for comprehension
I have found that Scala always has a "natural explanation" to anything. Always something like "ohh, but that's just a function being called on this and that object with this and that parameter". In a sense, nothing is really compiler-magic as we know it from other languages.
My question is on the <- operator as used in the following code:
for(i <- 0 to 10) println(i)
In this example I can see it being rewritten to something like:
0.to(10).foreach((i:Int)=>println(i))
but this doesn't explain how the i got carried into the anonymous function inside the foreach function. At the point where you write i it is not an object, and not yet a declared variable. So what is it, and how is it being carried over to the inside of foreach?
My guess is that I finally discovered something which is in fact compiler magic
Thanks for your time.
To clarify, my question is: how does the <- operator work in the 1st line of code since i is not an object on which it can be called as a function.
Solution 1:
To augment Dave's answer, here is a translation schema for 'for-comprehensions' from Scala language specification:
A comprehension
for (enums) yield e
evaluates expressione
for each binding generated by the enumerators enums. An enumerator sequence always starts with a generator; this can be followed by further generators, value definitions, or guards.A generator
p <- e
produces bindings from an expressione
which is matched in some way against patternp
. A value definitionval p = e
binds the value namep
(or several names in a patternp
) to the result of evaluating the expressione
. A guardif e
contains a boolean expression which restricts enumerated bindings.The precise meaning of generators and guards is defined by translation to invocations of four methods:
map
,filter
,flatMap
, andforeach
. These methods can be implemented in different ways for different carrier types.The translation scheme is as follows. In a first step, every generator
p <- e
, where p is not irrefutable (§8.1) for the type ofe
is replaced byp <- e.filter { case p => true; case _ => false }
Then, the following rules are applied repeatedly until all comprehensions have been eliminated.
A for-comprehension
for (p <- e) yield e0
is translated toe.map { case p => e0 }
.A for-comprehension
for (p <- e) e0
is translated toe.foreach { case p => e0 }
.A for-comprehension
for (p <- e; p0 <- e0 . . .) yield e00
, where . . . is a (possibly empty) sequence of generators or guards, is translated to:e.flatMap { case p => for (p0 <- e0 . . .) yield e00 }
.A for-comprehension
for (p <- e; p0 <- e0 . . .) e00
where . . . is a (possibly empty) sequence of generators or guards, is translated to:e.foreach { case p => for (p0 <- e0 . . .) e00 }
.A generator
p <- e
followed by a guardif g
is translated to a single generator:p <- e.filter((x1, . . . , xn) => g )
wherex1
, . . . ,xn
are the free variables ofp
.A generator
p <- e
followed by a value definitionval p0 = e0
is translated to the following generator of pairs of values, wherex
andx0
are fresh names:val (p, p0) <- for(x@p <- e) yield { val x0@p0 = e0; (x, x0) }
Solution 2:
<-
is a language-defined keyword symbol, as is =>
but in distinct contrast to ->
(which is a defined symbol). Because it is part of the basic Scala grammar, it can be used to create bindings (for the i
in your example) which is something that cannot be done by user-defined constructs.