views:

243

answers:

3

Hello guys,

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.

+4  A: 

In this case, it really is a bit of compiler magic. Translation from for-comprehension to filter/map/flatmap form is a special bit of desugaring, much like conversion of the special forms of update and apply methods.

Dave Griffith
+8  A: 

To augment Dave's answer, here is a translation schema for 'for-comprehensions' from Scala language specification:

A comprehension for (enums) yield e evaluates expression e 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 expression e which is matched in some way against pattern p. A value definition val p = e binds the value name p (or several names in a pattern p) to the result of evaluating the expression e. A guard if 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, and foreach. 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 of e is replaced by

 p <- 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 to e.map { case p => e0 }.

  • A for-comprehension for (p <- e) e0 is translated to e.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 guard if g is translated to a single generator:
    p <- e.filter((x1, . . . , xn) => g )
    where x1, . . . , xn are the free variables of p.

  • A generator p <- e followed by a value definition val p0 = e0 is translated to the following generator of pairs of values, where x and x0 are fresh names:

    val (p, p0) <- 
      for(x@p <- e) yield { val x0@p0 = e0; (x, x0) }
    
missingfaktor
Okay, I don't understand everything after first read, but it's interesting :-) Where did you get this?
Felix
@Felix: As I said at the top of the answer, it's from the language specification (can be downloaded from www.scala-lang.org)
missingfaktor
+5  A: 

<- 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.

Randall Schulz
This seems to be the answer. I would suggest you to document this if possible, it is kinda out of the blue right now.
Felix
@Felix: It's documented in the specification. And pretty much every Scala book available in the market covers it.
missingfaktor
oh yeah, i didnt get a book yet. waiting for the definitive 2.8 book
Felix
This is not at all new in 2.8. I only picked up the language in the 2.7 era, but I'm pretty sure it goes back much further, likely to the very beginning of the language.
Randall Schulz