Ленивая оценка, thunk и закрытие функций в Scala - PullRequest
2 голосов
/ 12 марта 2020
case class Test[A](elem: () => A)

object Fun extends App {

  def test1(v: => Int): Test[Int] = Test(() => v)

  val a1 = test1({ println("hello"); 1 })
  val a2 = a1.elem() //echoes hello
  val a3 = a1.elem() //echoes hello

  def test2(v: => Int): Test[Int] = {
    lazy val y = v
    Test(() => y)
  }

  val b1 = test2({ println("hello"); 1 })
  val b2 = b1.elem() //echoes hello
  val b3 = b1.elem() //doesn't echo hello. Is function closure at work here?
}

Test - это класс case, который принимает объект типа Function0[A] в качестве аргумента конструктора.

test1 использует не строгий параметр и возвращает экземпляр Test[Int]. когда a1 создан, он получает elem = () => { println("hello"); 1 }. И поэтому имеет смысл, когда hello печатается дважды, в то время как a2 и a3 создаются путем применения elem.

test2 также использует не строгий параметр и возвращает экземпляр Test[Int]. когда b1 создан, он получает elem = () => y. y не оценен и привязан к вызывающей стороне - test2. Когда elem применяется для создания b2, через elem(), y оценивается (и, таким образом, печатается hello ), а затем кэширует результат, равный 1. Последующий вызов elem() при создании b3 использует оцененное значение. Однако, поскольку y не является локальным по отношению к elem, единственный способ, с помощью которого все это может работать, - это закрытие.

Это точно?

Примечание: я просмотрел приведенный здесь пример : Scala ленивая оценка и применение функции но это не совсем то, что я пытаюсь понять

1 Ответ

2 голосов
/ 13 марта 2020

Вы можете увидеть реализацию захваченных элементов, используя scalac -Vprint:_.

В этом случае,

    def test2(v: Function0): Test = {
      lazy <artifact> val y$lzy: scala.runtime.LazyInt = new scala.runtime.LazyInt();
      new Test({
        (() => Fun.this.$anonfun$test2$1(y$lzy, v))
      })
    };

Ленивый локальный становится распределенным в куче со ссылкой, переданной в замыкание .

...