-62
4.1
Introduction
While the main theme of this thesis is laziness and how lazy languages can express image processing programs, the discussion so far has not particularly concerned lazy languages. That is, most functional languages, lazy or eager, and to some extent non functional languages may have the same benefits if programmed with the same principle.
In this chapter the discussion is focused on laziness. Rrst, laziness in the context of functional programming languages is described, where lazy evaluation is an implementation technique to allow non-strict semantics. Then, discussion moves on to the relationship between laziness and image processing. The meaning of the term laziness' is slightly different in the context of image processing, but it is a convenient feature to improve efficiency, and to improve modularity because progranrmners have to take little care in connecting functions together. The last part discusses algorithms which are considered to be particularly suitable if
expressed in l a ^ languages. We call tiiese algorithms inherently lazy and lazy languages can
express these algorithms in a natural fashion.
4.2
Lazy Evaluation
Lazy evaluation is an implementation technique of functional languages which allows non-
strict semantics. If a function is non-strict, even if it is applied to an ill-defined argument, such
as an error, undefined, or non-terminating, it may not produce an ill-defined result. As an example of non-strict functions, let us consider an infinite list with some ill-defined elements and a function to select an element from the list using list indexing:
list = [l/0,undef,sum[0..], 3]++[4.. ] select = (!)
where u n d e f is 'a completely und^ned value^ defined in M iranda which has a polymorphic
type, sum is applied to a list of numbers and returns their sum. [ 0 . . ] is an infinite list starting at 0 and increasing by 1.
Miranda «elect list 0
program error: attempt.to divide by zero
Miranda select list 1
program error : undefined
Miranda select list 2
« n o t enough heap space — task abandoned»
Miranda select list 3
3
Miranda select list 1000
1000
An expression that does not denote a well-defined value in the normal mathematical
sense is called bottom, written using the symbol _L. A function is said to be strict if it returns
bottom whenever any of its aiguments (or any part of its arguments) is equal to bottom. All other functions are non-strict. In the above example, the function s e l e c t is applied to l i s t which includes some elements which do not have a well-defined value, but s e l e c t can still return a well-defined value. So it is non-strict.
42.1 Elements of lazy evaluation
This subsection briefly describes how laziness is achieved in the context of implementation of lazy functional languages. For more details, see [Peyton Jones87a] and [Peyton Jones92a]. The typical implementation of lazy evaluation includes the following three techniques:
1. Normal order reduction
As most functional languages are based on the lambda calculus, the implementation is
usually explained as reduction of a lambda expression to its normal form. Normal order
reduction, or outermost reduction, describes the reduction strategy which reduces the
leftmost redex (reducible expression) first if there is more than one redex. It ensures that
an aig;ument is required only when it is necessary because, in function application, the outermost redex is the function application itself. So, normal order reduction always evaluates the function before evaluating its arguments.
2. G raph reduction
Let us look at an example first: given the function definition, s q r x = x * x, if we reduce the expression, s q r (4+2), normal order reduction requires more reduction
steps than its counterpart, called applicative order, or innermost reduction [BirdSSa]. This
- 6 4-
with a pointer to avoid the same expression from being evaluated more than once. In
this way, normal order graph reduction ensures that an argument is calculated only when
it is required, and that it is evaluated at most once. The num ber of reduction steps is never more than applicative order reduction.
3. Stop at weak head normal form
If, for example, an expression to be reduced consists of structured data, such as a list or a tree, it may not be necessary to reduce the term into the normal form because only some elements in the structure may be required. On the other hand, certain information
will always be required. So reduction should stop at a stage called weak head normal^rm,
— see, e.g. [Peyton Jones87a] for a definition. For example, an expression of the form
(expression! :expression2) is in weak head normal form even if each expression in the list
may not be normal form. The reduction does not proceed until which element Is required is known.
4.2.2 Space efficiency of lazy evaluation
If we talk about efficiency, not only speed but space consumption is also an important factor. As Hughes discussed [Hughes84a], if a program involves a simple flow of data, such as counting the num ber of characters in a file, lazy evaluation provides a space efficient solution. Whereas, if there is branching or merging of streams of data which requires synchronisation between the streams due to the difference of data consumption speed, lazy evaluation may accumulate intermediate data structures. He proposed the use of language constructs to control the behaviour of programs. However, it has to be adm itted that these constructs are
rather difhcult to use. Separately, Wadler tackled this problem and proposed the listless
transformer to automate elimination of intermediate structures [Wadler84a]. However, the
applicability of this technique is fairly limited. More recently, this problem is called
deforestation [WadlerSSa] to reduce intermediate tree structures and is still a difficult problem.
There is a lot of subtlety in space behaviour of lazy functional programs and it is difficult
to predict or optimise the memoiy usage. For example, space leaks [Peyton Jones87a]. One
possible suggestion from an application programmer's point of view is to use profiling tools [Runciman93a, Sansom93a] which provide information on space and time behaviour of a lazy functional program. Using this kind of tool, programmers m ay be able to analyse their programs and improve them (See Chapter 8).
4.2.3 Drawbacks of lazy evaluation
As shown above lazy evaluation ensures that any subexpression is not evaluated until it is required, and that it is evaluated at most once. Therefore, on the one hand, lazy evaluation is more efficient because it minimises the number of reduction steps. Also, if a program deals with only a simple flow of data stream, n^m ory usage should be efficient. On the other hand, apparently, it involves a bigger overhead because each time reduction proceeds the system has to check whether the expression is in weak head normal form and decide whether or not to proceed. So, a drawback, but quite a major one, would be its execution speed. It should be said that, as far as speed is concerned, lazy evaluation pays off when the saving effect is greater than the overhead cost However, it is not only efficiency but also modularity of programs that matters to image processing programming, as discussed in the following.