Simple example: Infinite sequence
Lazy evaluation allows the creation of things like infinite data structures. One example is the definition of an endless sequence:
ones creates an instance of
that starts with the head value
1 followed by a tail that
ones as well. The tail is constructed lazily through a function
ones only when needed.
We can now take an arbitrary number of elements from this sequence, e.g.,
using a call to
take as shown in the following example that creates
sequences of 5 and 50 elements:
What happens here?
On a call to
Cons-cell is created that
contains a head value
1 and a function to create the tail when
needed. This tail function calls
ones when the tail of this list
Infinite data structures like this may easily result in infinite loops or infinite recursion. It is, e.g., not possible to count the elements of the infinite list above:
or to convert that list into a string:
Lazy evaluation for performance
In addition to making infinite structure possible, another important aspect of lazy evaluation is that it avoids run-time overhead for the evaluation of values if these values are not needed. An example would be a logging mechanism as follows:
This code runs very slowly just because the logging messages are created.
A more efficient alternative uses lazy evaluation as follows:
In the lazy version, the
msg parameter for
no longer of type
String but now has type
String, i.e., a function producing a string. This function is called
implicitly when needed.
Additionally, the message parameter passed to
log now is a
Lazy String, which can be given as an ordinary string, or in
the form of a lambda
()->... which runs code to create a
string when called.