2
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 is a case class that takes an object of type Function0[A] as constructor argument.

test1 uses a non-strict parameter and returns an instance of Test[Int]. when a1 is created, it gets elem = () => { println("hello"); 1 }. And so it makes sense when hello gets printed twice while a2 and a3 are created by applying elem.

test2 also uses a non-strict parameter and returns an instance of Test[Int]. when b1 is created, it gets elem = () => y. The y is unevaluated and is bound to the caller - test2. When elem is applied to create b2, through elem(), y gets evaluated (and thus prints hello) and then caches the result which is 1. Subsequent call to elem() while creating b3 uses the evaluated value. However since y is not local to elem, the only way all this can work is through closure.

Is that accurate?

Note: I have gone through the example posted here : Scala lazy evaluation and apply function but it isn't exactly what I am trying to understand

Mario Galic
  • 47,285
  • 6
  • 56
  • 98

1 Answers1

2

You can see the implementation of captured elements using scalac -Vprint:_.

In this case,

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

The lazy local becomes heap-allocated, with a reference passed into the closure.

som-snytt
  • 39,429
  • 2
  • 47
  • 129