Lazy evaluation
Encyclopedia
In programming language theory
, lazy evaluation or call-by-need is an evaluation strategy
which delays the evaluation of an expression until the value of this is actually required (non-strict evaluation) and which also avoids repeated evaluations (sharing). The sharing can reduce the running time of certain functions by an exponential factor over other non-strict evaluation strategies, such as call-by-name.
The benefits of lazy evaluation include:
Lazy evaluation can lead to reduction in memory footprint, since values are created when needed. However, with lazy evaluation, it is difficult to combine with imperative features such as exception handling
and input/output
, because the order of operations becomes indeterminate. Also, debugging is difficult.
The opposite of lazy actions is eager evaluation
, sometimes known as strict evaluation. Eager evaluation is the evaluation behavior used in most programming languages.
by and for programming languages independently by and .
. 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
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 "
. Perl 6
uses lazy evaluation of lists, so one can assign infinite lists to variables and use them as arguments to functions, but unlike Haskell and Miranda, Perl 6 doesn't use lazy evaluation of arithmetic operators and functions by default.
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 number
s. 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:
In Haskell syntax, "
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.
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 the expected behavior is that
define f(x,y) = 2*x
set k = f(e,5)
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. While in
l' = if h then i else 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) have side effects or introduce run time errors, the subtle differences between (l) and (l') can be complex. As most programming languages are Turing-complete
, it is possible to introduce user-defined lazy control structures in eager languages as functions, though they may depart from the language's syntax for eager evaluation: Often the involved code bodies (like (i) and (j)) need to be wrapped in a function value, so that they are executed only when called.
Short-circuit evaluation of Boolean control structures is sometimes called "lazy".
s, 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.
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
Also, pattern matching in Haskell 98 is strict by default, so the
Design patterns
Blog posts by computer scientists
Programming language theory
Programming language theory is a branch of computer science that deals with the design, implementation, analysis, characterization, and classification of programming languages and their individual features. It falls within the discipline of computer science, both depending on and affecting...
, lazy evaluation or call-by-need is an evaluation strategy
Evaluation strategy
In computer science, an evaluation strategy is a set of rules for evaluating expressions in a programming language. Emphasis is typically placed on functions or operators: an evaluation strategy defines when and in what order the arguments to a function are evaluated, when they are substituted...
which delays the evaluation of an expression until the value of this is actually required (non-strict evaluation) and which also avoids repeated evaluations (sharing). The sharing can reduce the running time of certain functions by an exponential factor over other non-strict evaluation strategies, such as call-by-name.
The benefits of lazy evaluation include:
- Performance increases due to avoiding unnecessary calculations and avoiding error conditions in the evaluation of compound expressions.
- The capability of constructing potentially infinite data structureData structureIn computer science, a data structure is a particular way of storing and organizing data in a computer so that it can be used efficiently.Different kinds of data structures are suited to different kinds of applications, and some are highly specialized to specific tasks...
s - The capability of defining control structures as abstractions instead of as primitives.
Lazy evaluation can lead to reduction in memory footprint, since values are created when needed. However, with lazy evaluation, it is difficult to combine with imperative features such as exception handling
Exception handling
Exception handling is a programming language construct or computer hardware mechanism designed to handle the occurrence of exceptions, special conditions that change the normal flow of program execution....
and input/output
Input/output
In computing, input/output, or I/O, refers to the communication between an information processing system , and the outside world, possibly a human, or another information processing system. Inputs are the signals or data received by the system, and outputs are the signals or data sent from it...
, because the order of operations becomes indeterminate. Also, debugging is difficult.
The opposite of lazy actions is eager evaluation
Eager evaluation
In computer programming, eager evaluation or greedy evaluation is the evaluation strategy in most traditional programming languages. In eager evaluation an expression is evaluated as soon as it gets bound to a variable. The term is typically used to contrast lazy evaluation, where expressions are...
, sometimes known as strict evaluation. Eager evaluation is the evaluation behavior used in most programming languages.
History
Lazy evaluation was introduced for the lambda calculusLambda calculus
In mathematical logic and computer science, lambda calculus, also written as λ-calculus, is a formal system for function definition, function application and recursion. The portion of lambda calculus relevant to computation is now called the untyped lambda calculus...
by and for programming languages independently by and .
Applications
Delayed evaluation is used particularly in functional languagesFunctional programming
In computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state...
. 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
Subroutine
In computer science, a subroutine is a portion of code within a larger program that performs a specific task and is relatively independent of the remaining code....
or special syntax
Syntax of programming languages
In computer science, the syntax of a programming language is the set of rules that define the combinations of symbols that are considered to be correctly structured programs in that language. The syntax of a language defines its surface form...
to delay evaluation. In Miranda and Haskell
Haskell (programming language)
Haskell is a standardized, general-purpose purely functional programming language, with non-strict semantics and strong static typing. It is named after logician Haskell Curry. In Haskell, "a function is a first-class citizen" of the programming language. As a functional programming language, the...
, 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 (delayed computation). The object representing such an explicitly delayed evaluation is called a future or promiseFutures and promises
In computer science, future, promise, and delay refer to constructs used for synchronization in some concurrent programming languages. They describe an object that acts as a proxy for a result that is initially not known, usually because the computation of its value has not yet completed.The term...
. Perl 6
Perl 6
Perl 6 is a major revision to the Perl programming language. It is still in development, as a specification from which several interpreter and compiler implementations are being written. It is introducing elements of many modern and historical languages. Perl 6 is intended to have many...
uses lazy evaluation of lists, so one can assign infinite lists to variables and use them as arguments to functions, but unlike Haskell and Miranda, Perl 6 doesn't use lazy evaluation of arithmetic operators and functions by default.
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 number
Fibonacci number
In mathematics, the Fibonacci numbers are the numbers in the following integer sequence:0,\;1,\;1,\;2,\;3,\;5,\;8,\;13,\;21,\;34,\;55,\;89,\;144,\; \ldots\; ....
s. 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:
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
Fold (higher-order function)
In functional programming, fold – also known variously as reduce, accumulate, compress, or inject – are a family of higher-order functions that analyze a recursive data structure and recombine through use of a given combining operation the results of recursively processing its...
would result in the program either failing to terminate or running out of memory.
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 the expected behavior is that
define f(x,y) = 2*x
set k = f(e,5)
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. While in
l' = if h then i else 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) have side effects or introduce run time errors, the subtle differences between (l) and (l') can be complex. As most programming languages are Turing-complete
Turing completeness
In computability theory, a system of data-manipulation rules is said to be Turing complete or computationally universal if and only if it can be used to simulate any single-taped Turing machine and thus in principle any computer. A classic example is the lambda calculus...
, it is possible to introduce user-defined lazy control structures in eager languages as functions, though they may depart from the language's syntax for eager evaluation: Often the involved code bodies (like (i) and (j)) need to be wrapped in a function value, so that they are executed only when called.
Short-circuit evaluation of Boolean control structures is sometimes called "lazy".
Other uses
In computer windowing systemWindowing system
A windowing system is a component of a graphical user interface , and more specifically of a desktop environment, which supports the implementation of window managers, and provides basic support for graphics hardware, pointing devices such as mice, and keyboards...
s, 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
Copy-on-write
Copy-on-write is an optimization strategy used in computer programming. The fundamental idea is that if multiple callers ask for resources which are initially indistinguishable, they can all be given pointers to the same resource...
page allocation or demand paging
Demand paging
In computer operating systems, demand paging is an application of virtual memory. In a system that uses demand paging, the operating system copies a disk page into physical memory only if an attempt is made to access it...
, 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
Mmap
In computing, mmap is a POSIX-compliant Unix system call that maps files or devices into memory. It is a method of memory-mapped file I/O. It naturally implements demand paging, because initially file contents are not entirely read from disk and do not use physical RAM at all...
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.
Laziness in eager languages
Python:Category:Python (programming language)- In Python 2.x the
range
function computes a list of integers (eager or immediate evaluation):- >>>
r = range(10)
- >>>
print r
- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>>
print r[3]
- 3
- >>>
- In Python 3.x the
range
function returns an iterator which computes elements of the list on demand (lazy or deferred evaluation):- >>>
r = range(10)
- >>>
print(r)
- range(0, 10)
- >>>
print(r[3])
- 3
- >>>
- This change to lazy evaluation saves execution time for large ranges which may never be fully referenced and memory usage for large ranges where only one or a few elements are needed at any time.
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). Strict evaluation usually implies eagerness, but they are technically different concepts.However, there is an optimisation implemented in some compilers called strictness analysis
Strictness analysis
In computer science, strictness analysis refers to any algorithm used to prove that a function in a non-strict functional programming language is strict in one or more of its arguments. This information is useful to compilers because strict functions can be compiled more efficiently...
, 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. See also
- Combinatory logicCombinatory logicCombinatory logic is a notation introduced by Moses Schönfinkel and Haskell Curry to eliminate the need for variables in mathematical logic. It has more recently been used in computer science as a theoretical model of computation and also as a basis for the design of functional programming...
- CurryingCurryingIn mathematics and computer science, currying is the technique of transforming a function that takes multiple arguments in such a way that it can be called as a chain of functions each with a single argument...
- DataflowDataflowDataflow is a term used in computing, and may have various shades of meaning. It is closely related to message passing.-Software architecture:...
- Eager evaluationEager evaluationIn computer programming, eager evaluation or greedy evaluation is the evaluation strategy in most traditional programming languages. In eager evaluation an expression is evaluated as soon as it gets bound to a variable. The term is typically used to contrast lazy evaluation, where expressions are...
- Functional programmingFunctional programmingIn computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state...
- Futures and promisesFutures and promisesIn computer science, future, promise, and delay refer to constructs used for synchronization in some concurrent programming languages. They describe an object that acts as a proxy for a result that is initially not known, usually because the computation of its value has not yet completed.The term...
- Graph reductionGraph reductionIn computer science, graph reduction implements an efficient version of non-strict evaluation, an evaluation strategy where the arguments to a function are not immediately evaluated. This form of non-strict evaluation is also known as lazy evaluation and used in functional programming languages...
- Incremental computingIncremental computingIncremental computing, also known as incremental computation, is a software feature which, whenever a piece of data changes, attempts to save time by only recomputing those outputs which "depend on" the changed data....
– a related concept whereby computations are only repeated if their inputs change. May be combined with lazy evaluation. - Lambda calculusLambda calculusIn mathematical logic and computer science, lambda calculus, also written as λ-calculus, is a formal system for function definition, function application and recursion. The portion of lambda calculus relevant to computation is now called the untyped lambda calculus...
- Lazy initializationLazy initializationIn computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed....
- LookaheadLookaheadLookahead is a tool in algorithms for looking ahead a few more input items before making a cost effective decision at one stage of the algorithm.- Lookahead in search problems :...
- Short-circuit (minimal) evaluation
- Non-strict programming language
- Normal order evaluation
Further reading
PhD thesis, Oxford UniversityDesign patterns
- John HughesJohn Hughes (computer scientist)R. John M. Hughes is a computer scientist who does research in the field of programming languages and the author of several influential research papers on the subject, including "Why Functional Programming Matters". He is a professor in the department of Computing Science at the Chalmers University...
. "Why functional programming matters". The Computer JournalThe Computer JournalThe Computer Journal is a journal published by the Oxford University Press on behalf of the British Computer Society. It contains peer-reviewed articles and other contributions on computer science and information systems. Its first issue appeared in 1958, and it has been published continually since...
- Special issue on lazy functional programming. Volume 32 Issue 2, April 1989. - Philip WadlerPhilip WadlerPhilip Wadler is a computer scientist known for his contributions to programming language design and type theory. In particular, he has contributed to the theory behind functional programming and the use of monads in functional programming, the design of the purely functional language Haskell, and...
. "How to replace failure by a list of successes a method for exception handling, backtracking, and pattern matching in lazy functional languages". Functional Programming Languages and Computer Architecture. Lecture Notes in Computer Science, 1985, Volume 201/1985, 113-128.
Blog posts by computer scientists
- Robert HarperRobert Harper (computer scientist)Robert "Bob" William Harper, Jr. is a computer science professor at Carnegie Mellon University who works in programming language research. He made major contributions to the design of the Standard ML programming language and the LF logical framework....
. "The Point of Laziness" - Lennart AugustssonLennart AugustssonLennart Augustsson is a Swedish computer scientist. He was previously a lecturer at the Computing Science Department at Chalmers University of Technology...
. "More points for lazy evaluation"
External links
- Lazy Evaluation at the Portland Pattern RepositoryPortland Pattern RepositoryThe Portland Pattern Repository is a repository for computer programming design patterns. It was accompanied by a companion website, WikiWikiWeb, which was the world's first wiki....
- Lazy evaluation at Haskell Wiki
- Functional programming in Python becomes lazy
- Lazy function argument evaluation in the D programming languageD (programming language)The D programming language is an object-oriented, imperative, multi-paradigm, system programming language created by Walter Bright of Digital Mars. It originated as a re-engineering of C++, but even though it is mainly influenced by that language, it is not a variant of C++...
- Lazy evaluation macros in Nemerle
- Lazy programming and lazy evaluation in Scheme
- Lambda calculus in Boost Libraries in C++ programming language
- Lazy Evaluation in PerlPerlPerl is a high-level, general-purpose, interpreted, dynamic programming language. Perl was originally developed by Larry Wall in 1987 as a general-purpose Unix scripting language to make report processing easier. Since then, it has undergone many changes and revisions and become widely popular...