Scala's lazy arguments: How do they work?

python dude picture python dude · Mar 21, 2012 · Viewed 18.8k times · Source

In the file Parsers.scala (Scala 2.9.1) from the parser combinators library I seem to have come across a lesser known Scala feature called "lazy arguments". Here's an example:

def ~ [U](q: => Parser[U]): Parser[~[T, U]] = { lazy val p = q // lazy argument
  (for(a <- this; b <- p) yield new ~(a,b)).named("~")
}

Apparently, there's something going on here with the assignment of the call-by-name argument q to the lazy val p.

So far I have not been able to work out what this does and why it's useful. Can anyone help?

Answer

Rex Kerr picture Rex Kerr · Mar 21, 2012

Call-by-name arguments are called every time you ask for them. Lazy vals are called the first time and then the value is stored. If you ask for it again, you'll get the stored value.

Thus, a pattern like

def foo(x: => Expensive) = {
  lazy val cache = x
  /* do lots of stuff with cache */
}

is the ultimate put-off-work-as-long-as-possible-and-only-do-it-once pattern. If your code path never takes you to need x at all, then it will never get evaluated. If you need it multiple times, it'll only be evaluated once and stored for future use. So you do the expensive call either zero (if possible) or one (if not) times, guaranteed.