Каковы правила области видимости для vals в Scala for-comprehensions

Когда я использую val в for-comprehension, я получаю предупреждение:

предупреждение: ключевое слово val для понимания устарело

несмотря на производство в приложении синтаксиса спецификации.

Это говорит о том, что когда я делаю что-то вроде

for (x <- xs; a = x)

На самом деле я не ввожу переменную, например, если я делаю что-то вроде

for (x <- xs) yield { implicit val a = x; /* more */ }

где, как обычно, фигурная скобка начинает новую область видимости, где я могу ввести новый val или даже новый имплицит.

Что я на самом деле делаю с этим a?

Я потребляю пространство стека? Куча? Какой-то другой псевдоним?


person som-snytt    schedule 29.12.2012    source источник


Ответы (1)


Как и обычное определение val pat = expr, элемент слева от знака равенства — это просто шаблон.

Продукция Enumerator в спецификации синтаксиса показывает, что предложение в for-expr может быть генератором (a <- b), защитой if cond или val def a = b.

Части, которые могут быть произвольными выражениями, это b (как указано справа от <- и =) и условие.

Responder.exec использует условное выражение для выполнения произвольного кода, тривиально вычисляя значение true.

Это означает, что вы можете делать произвольные побочные эффекты из условного выражения:

// 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)

Точно так же val def обесахаривает следующим образом:

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) }

Подожди, правда?

scala> def f(vs: List[Int]) = for (a <- vs; b = a+1) yield b
f: (vs: List[Int])List[Int]

Для этого вам понадобится недавний ответ:

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       

Я вижу два вызова карты, для промежуточного выражения и для выхода.

При дальнейшем рассмотрении первый anonfun — это не Int => Int (т. е. a+1), а Int => (Int,Int).

Таким образом, введенное нами значение val просто передается как часть кортежа.

person som-snytt    schedule 29.12.2012