Scala Tutorial
Basics
Control Statements
OOP Concepts
Parameterized - Type
Exceptions
Scala Annotation
Methods
String
Scala Packages
Scala Trait
Collections
Scala Options
Miscellaneous Topics
In Scala, the lazy
keyword is used to delay the initialization of a value until it's accessed for the first time. This is particularly useful for expensive computations or initializations that might not be needed during the runtime of the program.
lazy val
:A lazy val
is a value that's evaluated only once, the first time it's accessed, and then cached for subsequent accesses. If it's never accessed, the computation never happens.
Here's a basic example:
lazy val expensiveComputation: Int = { println("Computing...") // Imagine some heavy computation here 42 } println("Before accessing the lazy val") val result = expensiveComputation println(s"Result: $result")
When you run the above code, the output will be:
Before accessing the lazy val Computing... Result: 42
Notice that the "Computing..." message is printed only when the expensiveComputation
is accessed, not before.
Scala allows for the definition of infinite sequences (or streams) using the Stream
class (in Scala 2.13, this concept is replaced by LazyList
). These sequences are only computed as they're accessed, which means they can be infinite.
Here's an example of an infinite sequence of Fibonacci numbers:
def fibonacciStream: Stream[BigInt] = { def tail(a: BigInt, b: BigInt): Stream[BigInt] = a #:: tail(b, a + b) tail(0, 1) } val fibs = fibonacciStream.take(10).toList println(fibs) // List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
The fibonacciStream
is an infinite sequence, but because it's lazy, you can take the first 10 elements without calculating the entire sequence (which would be impossible!).
lazy val
and Infinite Sequences:You can combine the two concepts by defining an infinite sequence as a lazy val
:
lazy val infiniteInts: Stream[Int] = Stream.from(1)
This sequence will generate increasing integers starting from 1, but it won't do anything until you access it. And even then, it will only compute as much as you ask for:
val firstFive = infiniteInts.take(5).toList println(firstFive) // List(1, 2, 3, 4, 5)
The lazy
keyword and infinite sequences are powerful tools in Scala's arsenal, allowing for efficient computations and enabling interesting functional programming patterns. When used wisely, they can lead to efficient, clean, and elegant code solutions.
Creating lazy evaluated variables in Scala:
lazy val lazyVariable: Int = { println("Initializing lazyVariable") 42 } // Accessing lazyVariable triggers its initialization val result = lazyVariable
Infinite sequences in Scala with lazy evaluation:
lazy val infiniteSequence: LazyList[Int] = LazyList.from(1) // Accessing elements lazily val firstElement = infiniteSequence.head
Lazy val vs. eager val in Scala:
val
is eagerly evaluated, while a lazy val
is lazily evaluated.val eagerVariable: Int = { println("Initializing eagerVariable") 42 } // eagerVariable is initialized immediately lazy val lazyVariable: Int = { println("Initializing lazyVariable") 42 } // lazyVariable is initialized only when accessed
How to use Lazy val for memoization in Scala:
lazy val cachedResult: Int = { println("Computing expensive operation") computeExpensiveOperation() } def computeExpensiveOperation(): Int = { // Expensive computation 42 } // Accessing cachedResult avoids recomputing val result = cachedResult
Infinite Streams and Lazy Lists in Scala:
val infiniteStream: Stream[Int] = Stream.from(1) val lazyList: LazyList[Int] = LazyList.from(1)
Handling infinite data structures with Lazy val in Scala:
lazy val infiniteData: LazyList[Int] = LazyList.from(1) // Accessing elements lazily from an infinite sequence val firstElement = infiniteData.head