Lazy Evaluation
Simple example: Infinite sequence
Lazy evaluation allows the creation of things like infinite data structures. One example is the definition of an endless sequence:
The feature ones
creates an instance of list
that starts with the head value 1
followed by a tail that
is ones
as well. The tail is constructed lazily through a function
call to 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 ones
, a 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
is requested.
Dangers
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:
Despite that, conversion into a string works because the output for infinite lists is cropped by default:
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 log
is
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.