Lazy evaluation
From Wikipedia, the free encyclopedia
This article does not cite any references or sources. Please help improve this article by adding citations to reliable sources (ideally, using inline citations). Unsourced material may be challenged and removed. (December 2007) |
Programming evaluation |
---|
In computer programming, lazy evaluation (or delayed evaluation) is the technique of delaying a computation until such time as the result of the computation is known to be needed.
The attractions of lazy evaluation include: performance increases due to avoiding unnecessary calculations, avoiding error conditions in the evaluation of compound expressions, the ability to construct infinite data structures, and the ability to define control structures as regular functions rather than built-in primitives.
Languages that use lazy actions can be further subdivided into those that use a call-by-name evaluation strategy and those that use call-by-need. Most realistic lazy languages, such as Haskell, use call-by-need for performance reasons, but theoretical presentations of lazy evaluation often use call-by-name for simplicity.
The opposite of lazy actions is eager evaluation, also known as strict evaluation. Eager evaluation is the evaluation behavior used in most programming languages.
Contents |
[edit] Delayed evaluation
Delayed evaluation is used particularly in functional languages. When using delayed evaluation, an expression is not evaluated as soon as it gets bound to a variable, but when the evaluator is forced to produce the expression's value. That is, a statement such as x:=expression;
(i.e. the assignment of the result of an expression to a variable) clearly calls for the expression to be evaluated and the result placed in x
, but what actually is in x
is irrelevant until there is a need for its value via a reference to x
in some later expression whose evaluation could itself be deferred, though eventually the rapidly-growing tree of dependencies would be pruned in order to produce some symbol rather than another for the outside world to see.
Some programming languages delay evaluation of expressions by default, and some others provide functions or special syntax to delay evaluation. In Miranda and Haskell, evaluation of function arguments is delayed by default. In many other languages, evaluation can be delayed by explicitly suspending the computation using special syntax (as with Scheme's "delay
" and "force
" and OCaml's "lazy
" and "Lazy.force
") or, more generally, by wrapping the expression in a thunk. The object representing such an explicitly delayed evaluation is called a future or promise.
Delayed evaluation has the advantage of being able to create calculable infinite lists without infinite loops or size matters interfering in computation. For example, one could create a function that creates an infinite list (often called a stream) of Fibonacci numbers. The calculation of the n-th Fibonacci number would be merely the extraction of that element from the infinite list, forcing the evaluation of only the first n members of the list.
For example, in Haskell, the list of all Fibonacci numbers can be written as
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
In Haskell syntax, ":
" prepends an element to a list, tail
returns a list without its first element, and zipWith
uses a specified function (in this case addition) to combine corresponding elements of two lists to produce a third.
Provided the programmer is careful, only the values that are required to produce a particular result are evaluated. However, certain calculations may result in the program attempting to evaluate an infinite number of elements; for example, requesting the length of the list or trying to sum the elements of the list with a fold operation would result in the program either failing to terminate or running out of memory.
[edit] Control structures
Even in most eager languages if statements evaluate in a lazy fashion.
if a then b else c
evaluates (a) then if and only if (a) evaluates to true does it evaluate (b) otherwise it evaluates (c). That is either (b) or (c) will not be evaluated. Conversely in an eager language
define f(x,y) = 2*x set k = f(e,f)
will still evaluate (e) and (f) when computing (k). However user defined control structures depend on exact syntax so for example
define g(a,b,c) = if a then b else c l = g(h,i,j)
(i) and (j) would both be evaluated in an eager language.
l' = if h then i then j
(i) or (j) would be evaluated but never both.
Lazy evaluation allows control structures to be defined normally, and not as primitives or compile-time techniques. If (i) or (j) had side effects or introduced run time errors the subtle differences between (l) and (l') can be complex. As most programming languages are Turing-complete, it is of course possible to have developers introduce their own lazy control structures in eager languages, either as built-ins like C's ternary operator ?: or by other techniques such as clever use of lambdas, or macros.
Short-circuit evaluation of Boolean control structures is sometimes called "lazy".
[edit] Controlling eagerness in lazy languages
In lazy programming languages such as Haskell, although the default is to evaluate expressions only when they are demanded, it is possible in some cases to make code more eager - or conversely, to make it more lazy again after it has been made more eager. This can be done by explicitly coding something which forces evaluation (which may make the code more eager) or avoiding such code (which may make the code more lazy). Eagerness is also known as strictness.
However, there is an optimisation implemented in some compilers called strictness analysis, which, in some cases, allows the compiler to infer that a value will always be used. In such cases, this may render the programmer's choice of whether to force that particular value or not, irrelevant, because strictness analysis will force strict evaluation.
In Haskell, marking constructor fields strict means that their values will always be demanded immediately. The seq
function can also be used to demand a value immediately and then pass it on, which is useful if a constructor field should generally be lazy. However, neither of these techniques implements recursive strictness - for that, a function called deepSeq
was invented.
Also, pattern matching in Haskell 98 is strict by default, so the ~
qualifier has to be used to make it lazy.
[edit] Other uses
In computer windowing systems, the painting of information to the screen is driven by "expose events" which drive the display code at the last possible moment. By doing this, they avoid the computation of unnecessary display content.
Another example of laziness in modern computer systems is copy-on-write page allocation or demand paging, where memory is allocated only when a value stored in that memory is changed.
Laziness can be useful for high performance scenarios. An example is the Unix mmap functionality. mmap provides "demand driven" loading of pages from disk, so that only those pages actually touched are loaded into memory, and unnecessary memory is not allocated.
[edit] See also
- Combinatory logic
- Currying
- Dataflow
- Eager evaluation
- Functional programming
- Graph reduction
- Incremental computing - a related concept whereby computations are only repeated if their inputs change. May be combined with lazy evaluation.
- Lambda calculus
- Lazy initialization
- Lookahead
- Minimal evaluation
- Non-strict programming language
- Normal order evaluation
[edit] External links
- Functional programming in Python becomes lazy
- Lazy function argument evaluation in the D programming language
- Lazy evaluation macros in Nemerle
- Lazy programming and lazy evaluation in Scheme
- Lazy argument evaluation in Io programming language
- Lazy evaluation list/set processing in C programming language