When I use a val in a for-comprehension, I get the warning:
warning: val keyword in for comprehension is deprecated
despite the production in the syntax appendix of the spec.
This suggests that when I do something like
for (x <- xs; a = x)
I'm not really introducing a variable, such as if I do something like
for (x <- xs) yield { implicit val a = x; /* more */ }
where, as usual, the brace starts a new scope where I can introduce a new val, or even a new implicit.
What am I really doing with that a
?
Am I consuming stack space? Heap? Some other kind of alias?
Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e , where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter.
yield keyword will returns a result after completing of loop iterations. The for loop used buffer internally to store iterated result and when finishing all iterations it yields the ultimate result from that buffer.
Like an ordinary val pat = expr
definition, the thing to the left of the equals sign is just a pattern.
The Enumerator production in the syntax spec shows that the clause in a for-expr can be a generator (a <- b)
, guard if cond
or val def a = b
.
The parts that can be arbitrary expressions are b
(as given to the right of <-
and =
) and the condition.
Responder.exec
takes advantage of the conditional to execute arbitrary code, while evaluating trivially to true
.
That means you could do arbitrary side-effects from a conditional:
// yucky, yet instructive
scala> val xs = List(1,2,3)
scala> def bar(implicit i: Int) = Some(i+1)
scala> implicit var imp: Int = 0
scala> for { a<-xs; if { imp=a; true }; b<-bar } yield b
res6: List[Int] = List(2, 3, 4)
Similarly, the val def desugars as follows:
tmp <- xs
a = f(tmp) // some arbitrary function of tmp
// amounts to
(tmp, a) <- for (x@tmp <- xs) yield { val x0@a=f(tmp); (x, x0) }
Wait, really?
scala> def f(vs: List[Int]) = for (a <- vs; b = a+1) yield b
f: (vs: List[Int])List[Int]
You'll need a recent repl to do this:
scala> :javap f
[snip]
public scala.collection.immutable.List<java.lang.Object> f(scala.collection.immutable.List<java.lang.Object>);
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
0: aload_1
1: new #16 // class $anonfun$f$1
4: dup
5: invokespecial #17 // Method $anonfun$f$1."<init>":()V
8: getstatic #22 // Field scala/collection/immutable/List$.MODULE$:Lscala/collection/immutable/List$;
11: invokevirtual #26 // Method scala/collection/immutable/List$.canBuildFrom:()Lscala/collection/generic/CanBuildFrom;
14: invokeinterface #32, 3 // InterfaceMethod scala/collection/TraversableLike.map:(Lscala/Function1;Lscala/collection/generic/CanBuildFrom;)Ljava/lang/Object;
19: checkcast #28 // class scala/collection/TraversableLike
22: new #34 // class $anonfun$f$2
25: dup
26: invokespecial #35 // Method $anonfun$f$2."<init>":()V
29: getstatic #22 // Field scala/collection/immutable/List$.MODULE$:Lscala/collection/immutable/List$;
32: invokevirtual #26 // Method scala/collection/immutable/List$.canBuildFrom:()Lscala/collection/generic/CanBuildFrom;
35: invokeinterface #32, 3 // InterfaceMethod scala/collection/TraversableLike.map:(Lscala/Function1;Lscala/collection/generic/CanBuildFrom;)Ljava/lang/Object;
40: checkcast #37 // class scala/collection/immutable/List
43: areturn
I see two invocations of map, for the intermediate expression and for the yield.
On further inspection, the first anonfun is not a Int => Int
(i.e., a+1
) but a Int => (Int,Int)
.
So the val we introduced is just getting passed around as part of a tuple.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With