• No results found

-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.

4 3

H ow Laziness Contributes to Image Processing