Scala's <-
arrow seems a bit strange. Most operators are implemented somewhere in the source as a function, defined on a data type, either directly or implicitly. <-
on the other hand only seems to unusable outside of a for
comprehension, where it acts as a syntactic element used to signal the binding of a new variable in a monadic context (via map
).
This is the only instance I can think of where Scala has an operator-looking syntactical element that is only usable in a specific context, and isn't an actual function.
Am I wrong about how <-
works? Is it a special case symbol used just by the compiler, or is there some way a developer could use this behavior when writing their own code?
For example, would it be possible to write a macro to transform
forRange (i <- 0 to 10) { print(i) }
into
{ var i = 0; while (i <= 10) { print(i) } }
instead of its standard map
equivalent? As far as I can tell, any usage of i <- ...
outside of a for
context causes an exception due to referencing an unknown value.
In short, yes <-
is a reserved operator in Scala. It's a compiler thing.
Foreach
There is a strong distinction between foreach
and for yield
, but the syntax is only syntactic sugar, transformed at compile time.
for (i <- 1 to 10) { statement }
expression is translated to:
Range.from(1, 10).foreach(..)
Multiple variables:
for (i <- 1 to 10; y <- 2 to 100) {..}
becomes:
Range.from(1, 10).foreach(
el => {Range.from(2, 100).foreach(..)});
With all the variations given by:
for (x <- someList)
= someList.foreach(..)
.
Put simply, they all get de-sugared to foreach
statements. The specific foreach
being called is given by the collection used.
For yield
The for yield
syntax is sugar for flatMap
and map
. The stay in the monad
rule applies here.
for (x <- someList) yield {..}
gets translated to a someList.flatMap(..)
.
Chained operations become hierarchical chains of map/flatMap
combos:
for {
x <- someList; y <- SomeOtherList
} yield {}
becomes:
someList.flatMap(x => {
y.flatMap(..)
});
and so on.
The point
The point is that the <-
operator is nothing more than syntactic sugar to make code more readable, but it always gets replaced at compile time.
To emphasize Rob's point
Rob makes excellent examples of other Scala syntactic sugar.
A context bound
package somepackage;
class Test[T : Manifest] {}
Is actually translated to:
class Test[T](implicit evidence: Manifest[T])
As proof, try to alias a type with a context bound:
type TestAlias[T : Manifest] = somepackage.Test // error, the : syntax can't be used.
.
It is perhaps easy to see how the : Manifest
part is actually not a type a parameter.
It's just easier to type class Test[T : Manifest]
rather than class Test[T](implicit evidence: Manifest[T]
.