Lazy Values, Views, and LazyList
Prerequisites: Collection operations
lazy val
A lazy val is computed the first time it's used, then cached:
object Settings:
lazy val config: Map[String, String] =
println("loading config...")
Map("mode" -> "production")
@main def lazyVals(): Unit =
println("start")
println(Settings.config("mode"))
println(Settings.config("mode"))
start
loading config...
production
production
Useful for expensive values that might not be needed.
Views
Collection operations normally build a new collection at each step. A view runs them lazily, element by element, only as far as needed:
@main def views(): Unit =
var checked = 0
val firstBig = (1 to 1_000_000).view
.map { n => checked += 1; n * n }
.find(_ > 50)
println(firstBig)
println(s"checked $checked elements")
Some(64)
checked 8 elements
Without .view, map would square all million numbers first.
LazyList: infinite sequences
@main def lazyLists(): Unit =
val naturals = LazyList.from(1)
println(naturals.filter(_ % 7 == 0).take(3).toList)
lazy val fibs: LazyList[BigInt] =
BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map(_ + _)
println(fibs.take(12).toList)
println(fibs(90))
List(7, 14, 21)
List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89)
2880067194370816120
A LazyList computes elements only when asked and remembers them. The
Fibonacci sequence is defined in terms of itself (declaring it as a
lazy val is what allows a local value to refer to itself) — only the
elements you request are ever computed.