Как ленивый val решает, что прямая ссылка распространяется на определение значения в scala - PullRequest
1 голос
/ 25 марта 2020

Ниже приведено определение типа потока, взятое из fp в scala главе 5

sealed trait Stream[+A]
case object Empty                                   extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]

. Мы можем написать следующий метод, который будет возвращать бесконечный поток

def constant[A](a: A): Stream[A] = {
    lazy val x: Stream[A] = Cons(() => a, () => x)
    x
  }

Что я не совсем понимаю, так это почему компилятор не генерирует прямая ссылка распространяется на определение значения , когда x определяется как lazy val (в противном случае это происходит)?

Я нашел старый пост здесь: https://www.scala-lang.org/old/node/6502 но все еще ищем четкое объяснение.

Ответы [ 2 ]

3 голосов
/ 25 марта 2020

Потому что теперь именно благодаря сообщению, которое вы связали (AFAIK), оно конкретно разрешено :

Объем имени, введенного объявлением или определением, является целым последовательность операторов, содержащая привязку. Однако существует ограничение на прямые ссылки в блоках: в последовательности операторов s1… sn, составляющей блок, если простое имя в si относится к объекту, определенному sj, где j≥i, то для всех sk между и включая si и sj,

  • sk не может быть определением переменной.

  • Если sk является определением значения, оно должно быть ленивым .

0 голосов
/ 25 марта 2020

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

Более того, вы также можете сделать так, чтобы они работали как значения, но они не могут быть локальной переменной. Посмотрите, что

class Test {
   val a = 5
   val x: Stream[Int] = Cons(() => a, () => x)
}

компилируется просто отлично.

...