Expression Re nement: Deriving Bresenham's Algorithm Alexander Bunkenburg and Sharon Flynn
Computing Science Department, University of Glasgow, Scotland fbunkenba,
[email protected] http://www.dcs.glasgow.ac.uk/~fbunkenba,sharong
Abstract
This paper illustrates a method of transforming an initial speci cation expression, which is not necessarily algorithmic, into an ecient functional implementation using a re nement calculus for expressions. In doing this, we bene t from the ease of manipulation that state-less expressions allow. However, implementations of functional algorithms are not as cheap as imperative implementations. We further show how an imperative program can be derived from a functional expression using algebraic transformations based on the state monad model. The example used to illustrate the method is Bresenham's line drawing algorithm.
1 Introduction In this paper we show how a simple speci cation expression can be re ned to an ecient functional program and then further transformed into an imperative program which is cheaper to implement than its functional equivalent. For the purpose of derivation, expressions are easier to manipulate than statements. This can be seen, for example, in the Bird-Meertens formalism [Mee86, Bir86, Bac89], `Squiggol', where obviously correct functional programs are transformed into more ecient programs using derivation steps which are equalities and which preserve correctness. Moreover, it is often the case that problems can be speci ed more naturally and elegantly using expressions rather than imperative commands. The re nement calculus for expressions used in this paper, based on the re nement calculus for imperative programming [Mor90, Mor89], is being developed to allow the re nement of possibly non-algorithmic speci cation expressions to executable expressions of a functional programming language, using a set of re nement laws. Unfortunately, implementations of functional algorithms are not as cheap as imperative implementations, where cheap means fast and using predictably little memory. The use of the state monad [Wad92, PJW93, Lau93] permits the capture of imperative programming in a functional language, which guarantees a safe imperative evaluation. This paper illustrates a method of deriving a correct imperative program from a certain form of functional speci cation, making use of the state monad. Supported by a postgraduate research studentship from the Engineering and Physical Sciences Research Council.
1
This paper describes an approach to program derivation using the example of Bresenham's line drawing algorithm [SS87, Bre65]. In [Spr82] this example is used to demonstrate program derivation by program transformation, using a form of Pascal extended with real numbers (as opposed to oating point numbers). The transformation steps are justi ed informally. In [Sne93] the derivation is also presented in an imperative guarded-command language. Its semantics is given by Hoare-triples. Our aim in this paper is to derive the algorithm formally, using expression re nement. An imperative program is then derived algebraically using the state monad model for imperative programs.
1.1 Outline of the paper
Section 2 describes the expression language FSL used to specify the problem, and some laws of the re nement calculus which are used later in the derivation of Bresenham's line drawing algorithm. The higher-order functions map and iterate are de ned and two theorems are introduced which allow expressions involving map to be transformed into expressions involving iterate. Section 3 introduces the line drawing problem, and an initial speci cation is given. This is then transformed into an ecient functional implementation of Bresenham's algorithm involving the iterate function, using laws from the re nement calculus for expressions. Section 4 describes how an `imperative' style of programming can be achieved in a functional language using the state monad and, in particular, how a correct imperative program can be derived from a functional expression involving iterate. Section 5 applies this derivation style to the functional description of Bresenham's algorithm, resulting in an imperative implementation.
2 Expression Re nement
2.1 The Expression Language and Re nement Laws
In the rst half of this paper we shall use the notation of FSL (Functional Speci cation Language), a language of expressions which is based on familiar mathematical notation. The language is intended to form part of a re nement calculus for functional programs, with a re nement relation between expressions and a set of re nement laws to allow the formal derivation of programs from speci cations. We do not intend to give a full description of FSL here1 . We introduce new notation alongside a small selection of laws of the re nement calculus which are used in the derivation in section 3. We simply state these laws, though they have been proved formally. Although all steps in the derivation presented here are equalities, we give an intuitive notion of re nement. Ultimately, we have that a speci cation is re ned by the program that implements it, written `S vP'. On a smaller scale, and since the process of re nement is stepwise, we have that expression E is re ned by expression F, `E vF', if every possible evaluation of F is a possible 1
The interested reader should apply to
[email protected]
evaluation of E. In general, F is more algorithmic, or its evaluation is more ecient, than E. In the following, we will use the equivalence sign `' as a meta-equivalence, a relation between expressions. We have the property that if E vF and F vE, then E F. Almost all constructs of FSL are monotonic with respect to re nement. For example, we have the following law for function application Law 1 (mono replacement) If E v F then f E v f F In the example of Bresenham's line drawing algorithm, de nedness is not a problem: all expressions are well-de ned. In addition, almost all expressions are deterministic. However, the choice operator for non-determinacy is used, as we shall see, to build conditional expressions. The non-deterministic choice between two expressions E and F of the same type, written E [] F, can evaluate to the result of evaluating E or to the result of evaluating F and we don't know, or care, which. Choice enjoys the properties of idempotency, commutativity and associativity and it has an identity which is the empty expression, empty. In terms of re nement we have the basic law for reducing non-determinacy in a speci cation: Law 2 (reduce non-det) E [] F v E A guarded expression is of the form B ! E, where B, the guard, is an expression of type Bool and E is an expression of some type T. The meaning of a guarded expression can be given by the following equivalences: true ! E E false ! E empty ?Bool ! E ?T where ?T is the unde ned element of type T. We will insist that guards are deterministic. Guard information can be used in the re nement of an expression: Law 3 (guard info) If B implies that EvF , then (B ! E) v (B ! F). Guarded expressions can be combined using the choice operator resulting in alternation expressions of the form if B1 ! E1 [] : : : [] Bn ! En When all guards are well-de ned, this chooses an Ei for which the corresponding Bi evaluates to true. If none of the Bi evaluates to true then the expression is unde ned. For convenience we de ne a conditional expression if B then E1 else E2 =^ if B ! E1 [] :B ! E2 A conditional expression is introduced in a re nement by the law: Law 4 (if intro) For B well-de ned, E if B then E else E. and can be re ned using: ie.
Law 5 (if re nement) (if B then E1 else E2 ) v (if C then F1 else F2) if (B ^ C) implies E1vF1, (B ^ :C) implies E1vF2 , (:B ^ C) implies E2vF1 and (:B ^ :C) implies E2vF2. Function application distributes through conditional expressions: Law 6 (app thru if) For B well-de ned, f(if B then E else F) (if B then f E else f F) The `let' construct is used to introduce local names into speci cations. We de ne let x = E in F ni =^ (fun x 2 T : F)E where T is the type of E. The `let' expression cannot be used to introduce recursion or any new expressive power to the language. It is used to add clarity to speci cation. Function application distributes through let: Law 7 (app thru let) f (let x = E in F ni) let x = E in f F ni if the variable x is not free in f .
2.2 Preliminaries
The types of the expression language FSL include lists, which can be considered as sequences. We assume that the higher order function map is familiar and note the following properties: Law 8 (functor map) map (f g) map f map g map id id where id is the identity of function composition `'. Our aim is to transform programs of the shape map f [i::k] into more iterative programs, where calculation of f(j + 1) can re-use some of the work that went into calculating f j, for integer j such that i j < k. Suppose that calculating f(j + 1) from f j is performed by applying a (simple) function, called next say, to f j, (8j i j < k ) f(j + 1) = next(f j) then we would only have to apply f once, namely to i, the rst integer in the range [i::k]. After that, we could simply keep applying next. We stop if we have a result for each integer in the range [i::k]. We can express this idea formally using the functions iterate and take, which are found in the standard Haskell prelude [Haskell report], and can be de ned in any lazy functional language. They are de ned as: ie.
De nition 1
take : N ! [a] ! [a] take 0 as =^ [] take n [] =^ [] take (n + 1) (a : as) =^ a : take n as
De nition 2
iterate : (a ! a) ! a ! [a] iterate f a =^ a : iterate f (fa) The function take receives a natural number n, say, and a list, and returns a list of the rst n elements of the list. If the list has fewer than n elements, the whole list is returned. The function iterate receives a function f with equal domain and range type, and a value a of that type. It returns the in nite list [a; fa; f(fa); f(f(fa)); :::]. Although this list is in nite, in a lazy language it is only calculated on a need-to-know basis. Typically one applies take n to the in nite result of iterate f a, and receives a well-de ned nite result. In the expression take n (iterate f a) laziness allows us to separate the loop, represented by iterate f, from the halting condition of the loop, represented by take n. Finally, #xs stands for the length of the list xs. The length of an integer range is, of course, trivial to calculate: #[x::y] = max(0; y ? x + 1). We are now ready to formalise the transformation described above.
Lemma 1 (Map to iterate) map f [x::y] (take#[x::y] iterate next)(f x) if x i < y implies f(i + 1) = next(f i)
The proof is by induction on the length of the list. To make this a little more general, we need another lemma:
Lemma 2 (Map and take commute) map f take n take n map f
These two functions on lists are the same. The proof is by induction on their argument list. Now we generalise lemma `map to iterate' by precomposing both sides with map use, and using the previous lemma:
Theorem 1 (Map to iterate) map(use make)[x::y] (take#[x::y] map use iterate next)(make x) if x i < y implies make(i + 1) = next(make i).
This theorem says that to map a function f over an integer range, all we have to do is nd three functions, here called make, use and next, such that use composed with make is our original function f, and next captures a recurrence relation on make. Typically make will do what f does, and some extra work, whereas use is usually a simple function like a projection from a tuple. Naturally this theorem only reduces work if the function next is simpler than f.
3 Line drawing
Given two integer pairs, (x1; y1) and (x2; y2), the line drawing problem is to nd the pixels which best approximate the line segment between them. The mathematical representation of the (in nite) line is de ned by the equation f x =^ y1 + m (x ? x1) (1) where m is the slope of the line, and can be calculated from m =^ (y2 ? y1)=(x2 ? x1) For convenience, we use the following abbreviations dy =^ y2?y1 and dx =^ x2?x1. However, the points of a mathematical line are given by pairs of real numbers, while pixels are pairs of integers. We would like to calculate those pixels which are nearest to the mathematical line. Let us assume, for simplicity, that the value of the slope of the line is between 0 and 1, 0 m 1. Other line segments can easily be obtained by symmetry. The problem is to nd, for the list of integer x-values [x1::x2], those y-values which best approximate the mathematical line given by (1). Bresenham's line drawing algorithm [Bre65] solves this problem using only integer arithmetic. We aim to derive this algorithm formally. Since 0 m 1, the line segment will be represented well if every x 2 Z between x1 and x2 is paired with some y 2 Z closest to f x. For convenience we de ne n =^ #[x1::x2]. Thus our initial speci cation is given by the expression map (round f) [x1::x2] (2) which computes the integer y-values for [x1::x2]. The function round : IR ! Z, which is total on the Real numbers and is deterministic, is de ned by round x =^ if x ? bxc > 0:5 then bxc + 1 else bxc (3) The oor of x 2 IR, denoted bxc, satis es the usual properties bxc x < bxc + 1 (4) There are two problems with our initial speci cation. The rst is that it uses real arithmetic, but takes as input and output only integers. We would prefer to use integer arithmetic only. Secondly, the algorithm is inecient, since f is being applied to each member of the list [x1::x2]. From the discussion in section 2.2, we can use the `Map to iterate' theorem 1 to transform the expression to a more ecient implementation if we can nd a recurrence relation for f. Fortunately, such a relation exists, from simple mathematics: f(x + 1) f x + m (5) Since this holds, in particular for x1 x < x2 we can apply the theorem with: next =^ (fun x 2 Z : x + m) make =^ f use =^ id So, we get:
map(round f)[x1::x2] law 8 `functor map' map round (map f[x1::x2]) `Map to iterate' theorem 1, f x1 = y1 map round (let next = (fun x 2 Z : x + m) in (take n map id iterate next)y1 ni) law 7 `app thru let', law 8 `functor map' let next = (fun x 2 Z : x + m) in (map round take n iterate next)y1 ni `Map and take commute' lemma let next = (fun x 2 Z : x + m) in (take n map round iterate next)y1 ni
which is an implementation of speci cation (2). Unfortunately this algorithm uses real arithmetic, calculating with real numbers which are not needed in the nal solution. We aim to derive Bresenham's line drawing algorithm [Bre65], which uses integer arithmetic only. If we de ne r : Z ! Z as follows: r i =^ round(f i) (6) then our initial speci cation (2) looks like map r [x1::x2] (7) We can use the `Map to iterate' theorem 1 if we can nd a recurrence relation for r, preferably using integer arithmetic only. Consider: r(x + 1) de nition of r (6) (round f)(x + 1) de nition of round (3) if f(x + 1) ? bf(x + 1)c > 0:5 then bf(x + 1)c + 1 else bf(x + 1)c v law 5 `if-re nement', proof requirements below if f(x + 1) ? r x > 0:5 then r x + 1 else r x for a suitable e, see below if e x < 0 then r x + 1 else r x Law 5 may be applied in the above derivation only if the four proof requirements are satis ed. These are of the following form: f(x + 1) ? bf(x + 1)c > 0:5 ^ f(x + 1) ? r x > 0:5
)
bf(x + 1)c + 1 r x + 1
There are four such proof requirements, one for each of the four alternatives. These can be proved using the properties of oor (4) and arithmetic. This step of the derivation, involving law 5, was initially motivated by knowledge of Bresenham's algorithm. Basically, since the slope of the line is between 0 and 1, the next y-value, r(x + 1), must be either the same as the previous value, r x, or its successor, r x+1. This step can be justi ed by noting the movement towards a recurrence relation for r. So, we have a recurrence relation for r, but it depends on the value of e x. We nd a suitable expression for e x:
ex 0:5 de nition (1) of f y1 + m (x + 1 ? x1) ? r x > 0:5 m = dy =dx, multiply by dx dx y1 + dy (x + 1 ? x1) ? dx r x > 0:5 dx arithmetic 2 dx r x + dx ? 2 dx y1 ? 2 dy (x + 1 ? x1) < 0
So, we de ne e x =^ 2 dx r x + dx ? 2 dx y1 ? 2 dy (x + 1 ? x1) (8) The function e, as for f, also satis es a recurrence relation: e(x + 1) = e x + 2 dx (r(x + 1) ? r x) ? 2 dy (9) Note that this expression for e uses integer arithmetic only. We can now eliminate r from the recurrence relation for e. We have already seen that the dierence between r(x + 1) and r x is always either 0 or 1. This information is used in the following: e(x + 1) recurrence de nition (9) e x + 2 dx (r(x + 1) ? r x) ? 2 dy law 4 `if intro' if e x < 0 then e x + 2 dx (r(x + 1) ? r x) ? 2 dy else e x + 2 dx (r(x + 1) ? r x) ? 2 dy v law 3 `guard info', and previous observations if e x < 0 then e x + 2 dx ? 2 dy else e x ? 2 dy and we know from de nition (8) that e x1 = dx ? 2 dy . Now we have that the calculation of the next y-value, r(x + 1), depends on the previous y-value, r x, and the dierence value e x. Therefore, at each iteration, we would want to calculate r(x + 1) and the next dierence value e(x + 1). Let us de ne a function k : Z ! Z Z forming the pair: k x =^ (r x; e x) (10) and combine the two recurrence relations into one: (r(x + 1); e(x + 1)) recurrence relations (if e x < 0 then r x + 1 else r x; if e x < 0 then e x + 2 dx ? 2 dy else e x ? 2 dy ) `product formation thru if', variation of law 6 if e x < 0 then (r x + 1; e x + 2 dx ? 2 dy ) else (r x; e x ? 2 dy ) Now we can use the `Map to iterate' theorem 1 with: next =^ (fun r; e 2 Z : if e < 0 then (r + 1; e + 2 dx ? 2 dy ) else (r; e ? 2 dy )) make =^ (fun x 2 Z : (r x; e x)) use =^ fst which gives us, from our rst speci cation (2):
map(round f)[x1::x2] de nitions of r (6) and k (10) map(fst k)[x1::x2] `Map to iterate' theorem 1 let next = (fun r; e 2 Z : if e < 0 then (r + 1; e + 2 dx ? 2 dy ) else (r; e ? 2 dy )) in (take n map fst iterate next)(y1; dx ? 2 dy ) ni
This implementation of speci cation (2) is ecient and uses only integer arithmetic. It corresponds to Bresenham's line drawing algorithm [Bre65].
4 Imperative Expressions 4.1 Language and Laws
Our approach to imperative programs is a simpli ed form of that used in [PJW93, Lau93, LPJ94], based on the state monad [Wad92]. We simplify because we are interested in program derivation, and not so much in semantics. We regard the state as a mapping from references to values. A reference to a value is the address of, or a pointer to, a value. We can change the value to which a reference points, change what the state maps the reference to, and still the reference itself stays unchanged. If a reference has type Ref A, then the value it references has type A. The state may be changed by special expressions called state transformers. Apart from delivering a value, like all expressions, these special expressions also take a state and deliver a state, so their type is State ! A State, which will be abbreviated by ST A, where A is the type of the value. We say a state transformer of type ST A returns a value of type A. We are not concerned with the precise meaning of State, but rather rely on intuition. In a program, the state cannot be bound to a formal variable. In the de nitions below, we allow the state to be bound, but only to the unusual identi er to emphasize that this is for de nitions only, and illegal in a program. These four primitives make state transformers that create a new reference, put a value into the place to which the reference points, get the value to which a reference points, and return a value without using the state: ie.
De nition 3 new new a put put v a get get v return return a
: A ! ST(Ref A) =^ fun 2 State : let v 2 Ref A n dom in (v; [v 7! a]) ni : Ref A ! A ! ST 1 =^ fun 2 State : ((); [v 7! a]) : Ref A ! ST A =^ fun 2 State : ( v; a) : A ! ST A =^ fun 2 State : (a; ) The new reference returned by new a must be of type Ref A, where a : A. Any reference of that type will do, as long as it is not already used in the state
, it is not in the domain of . (The backslash in the de nition denotes set subtraction.) Therefore the de nition of new a contains nondeterminacy. In the de nitions of new and put , [v 7! a] is the state that maps reference v to a and otherwise coincides with . The state transformer put v a changes the state, but has nothing interesting to return. So we'll just let it return the empty tuple () of type 1. Two state transformers may be composed in sequence by the (in x) primitive semicolon, often called bind: ie.
De nition 4
(; ) : ST A ! (A ! ST B) ! ST B m; k =^ fun :let (a; ) = m in k a ni: A word about strictness: We assume that new , get , and put are strict in the state, and in the references, but lazy in the values stored. The combinators return and semicolon are lazy in all arguments. The triple (ST; return ; (; )) is the state monad of [Wad92], and thus we may immediately conclude the monad laws: 0
0
Law 9 (monad laws) return E; F F E E; return E (E; fun x:F); G E; fun x:(F; G); where x may be free in F , but not in G.
Notice that the scope of a fun extends `as far as possible' and so the brackets on the right side of the third formula are super uous. The monad laws form the start of a collection of algebraic laws about state transformers. As an example of composing state transformers, the de nition of the combinator modify, which applies a function to the contents of a reference is: modify : (A ! A) ! Ref A ! ST 1 modify f v =^ get v; fun a:put v (f a): The programmer may only use state implicitly via combinations of semicolon and the primitive state transformers. This ensures that the state cannot be duplicated, and thus it may be implemented directly using the memory of the machine. The state monad is nothing more than a convenient abstract data type. A convenient combinator of state transformers is for. It takes a natural number n, and a state transformer k, and delivers a state transformer that is the composition of n copies of k, and returns the list of their results. The de nition of for is:
De nition 5 (for)
for : IN ! ST A ! ST [A] for 0 k =^ return [ ] for (n + 1) k =^ k; fun x:for n k; fun xs:return (x : xs):
A state transformer may be made into a state-less expression by applying
run to it: De nition 6 (run) run : ST A ! A run k =^ let 2 State in fst(k ) ni: The state is arbitrary. The expression run k applies the state transformer k to an arbitrary state, thereby obtaining a value-state pair. The state is discarded, and the value is the outcome of run k. We say that run encapsulates the state. It is immediate that run composed with return is the identity function: Law 10 (run-intro) E run(return E):
This is the rst step in making a functional program imperative. The second step is to introduce a reference: Law 11 (new -intro) run E run(new F; fun v:E); where v is not free in state transformer E . A function applied to the result of a run can be pushed into the run: Law 12 (function into run) f(run E) run(E; fun x:return (f x)): This law is used to move algorithmic work from the `functional' to the `imperative' part of the program. Another simple law that moves algorithmic work into the imperative part is: Law 13 (let =return ) let x = E in F ni return E; fun x:F; where F is a state transformer.
Two further simple laws are:
Law 14 (new generates put ) new E; fun v:put v F; return v new F and
Law 15 (new generates get ) new E; fun v:get v new E; fun v:return E: The rst says that initialising a new reference to E and then overwriting it by F is the same as initialising it to F immediately. The second says that putting a value into a new reference and retrieving it is the same as just putting it into a new reference and returning it. Many laws are concerned with changing the order of two state transformers that don't interfere with each other, for example:
Law 16 (get , put commute) get v; fun x:put w E; fun :return x put w E; fun :get v;
where x must not be free in E, and v and w are distinct. So far all proofs are straightforward from the de nitions. The laws mentioned could be used directly to derive an imperative program from a functional one. An imperative program part is introduced via `run-intro', work is moved into the imperative part via `function into run' and `let =return ', and from the trivial state transformer `return ' to real use of the state via `new generates get ' and `new generates put ', after new references have been introduced via `new intro'. We don't use this strategy directly, but rather derive an imperative implementation for the higher order function iterate, and then use that as a standard translation from functional programs built with iterate to imperative ones.
4.2 Imperative implementation of iterate Recall the de nition of iterate:
De nition 7 (iterate)
iterate : (A ! A) ! A ! [A] iterate f a =^ a : iterate f (f a): Typically take n for some natural n is used to extract a nite pre x of the in nite result of iterate f a. Lemma 3 (imperative iterate) The imperative implementation of (take n iterate f) a is: run(new a; fun v: for n get v; fun a: put v (f a); fun : return a ): The proof is by induction on n. In each step, we will refer to the laws used, except for the monad laws, which will be used often, without mention. Base case n = 0 (take 0 iterate f) a def. take [] run-intro, new -intro run(new a; fun v:return [ ]) def. for run(new a; fun v:for 0 (get v; fun a:put v (f a); fun :return a)) The inductive hypothesis is 9n 2 IN:8a:: (take n iterate f) a
run(new a; fun v:for n (get v; fun a:put v (f a); fun :return a)):
Step case n + 1
(take (n + 1) iterate f) a def. iterate take (n + 1) (a : iterate f (f a)) def. take a : ((take n iterate f) (f a)) inductive hypothesis, abbreviating a : run(new (f a); fun v:for n (:::)) function into run run(new (f a); fun v:for n (:::); fun xs:return (a : xs)) new generates put run(new a; fun v:put v (f a); fun :for n (:::); fun xs:return (a : xs)) let -intro, let =return run(new a; fun v:return a; fun x:put v (f x); fun : for n (:::); fun xs:return (x : xs)) new generates get , expanding abbreviation run(new a; fun v:get v; fun x:put v (f x); fun : for n (get v; fun a:put v (f a); fun :return a); fun xs:return (x : xs)) monad laws, def. for run(new a; fun v:for (n + 1) (get v; fun a:put v (f a); fun :return a))
Lemma proven by induction. We require another lemma:
Lemma 4 (map=for) for n K; fun as:return (map f as) for n (K; fun a:return (f a))
. The proof is by induction on n. Combining the lemmata `imperative iterate' and `map=for', we derive an imperative translation for an expression of the form delivered by theorem `map to iterate' 1, namely (take nmap useiterate next) init, for some n; use; next, and init:
Theorem 2 (take n map use iterate next) init run(new init; fun v: for n get v; fun x: put v (next x); fun : return x ):
We will use this theorem to translate the functional program into an equivalent imperative one.
5 Line drawing
The functional program that calculates the vertical coordinates of the line from (x1; y1) to (x2; y2) is: (take #[x1::x2] map fst iterate next) (y1; dx ? 2 dy ); where next is de ned next : Z Z ! Z Z next (r; e) =^ if e < 0 then (r + 1; e + 2 dx ? 2 dy ) else (r; e ? 2 dy ): We use the theorem to derive:
run(new (y1; dx ? 2 dy ); fun v: for #[x1::x2] get v; fun (r; e): (if e < 0 then put v(r + 1; e + 2 dx ? 2 dy ) else put v(r; e ? 2 dy ) ); fun : return r
): We have moved the function put v into the conditional expression for clarity, using law 6 (`function into if '). Further improvements would be to replace the reference v to a pair of values by two references to single values. This improvement is a data re nement, and won't be discussed here, however the result is given below.
run(new y1; fun vr:new (dx ? 2 dy); fun ve: for #[x1::x2] get vr; fun r:get ve; fun e: (if e < 0 then put vr(r + 1); fun :put ve(e + 2 dx ? 2 dy) else put ve(e ? 2 dy ) ); fun : return r ):
The state in this program is encapsulated by run, and the whole program delivers a list of integers, being approximate vertical coordinates for [x1::x2]. But we would probably want to display the line on a screen rather than just calculate a list of points. The state of the screen cannot be encapsulated. Therefore we rewrite the program so that the whole program itself is one state transformer returning nothing interesting (the empty tuple), but aecting the state of the screen. Let's assume the state transformer out : Z Z ! S 1 displays a pixel on the screen. We also add a reference to keep track of the horizontal coordinate x.
new x1; fun vx:new y1; fun vr:new (dx ? 2 dy); fun ve: for #[x1::x2] get vx; fun x:put vx (x + 1); fun : get vr; fun r:get ve; fun e: (if e < 0 then put vr(r + 1); fun :put ve(e + 2 dx ? 2 dy ) else put ve(e ? 2 dy ) ); fun : out(x; r); fun : return () This is the nal program, an imperative version of Bresenham's line drawing algorithm that displays the line on a screen.
6 Conclusions In this paper we have shown, using the example of Bresenham's line drawing algorithm, how a non-algorithmic mathematical speci cation expression can be transformed into an ecient functional program, using expression manipulation, and then further to a correct imperative program, using algebraic methods and the state monad. The speci cation language of expressions, FSL, and the associated re nement calculus for expressions are used for the rst part of this process. Much of the transformation is guided by knowledge of the algorithm itself, but each step is justi ed by re nement laws or theorems. In deriving the functional program from the speci cation, we bene t from the ease of manipulation that state-less expressions allow. Because manipulating imperative programs is more cumbersome than manipulating state-less expressions, we translate the functional program built from iterate into an imperative program by appealing to a theorem. Other combinators suitable for this method are until, the scan-family [Lau93, O'D93], and the fold-family. But is the nal program really an imperative program? After all, the definitions and transformations use laziness, whereas traditional imperative languages are strict, thereby guaranteeing the desired predictably small usage of memory. Before we execute the derived program in a strict manner, we had better make sure that it doesn't give non-termination. Fortunately for the current program there is no problem, but in general we haven't addressed that question. The state monad seems a reasonably practical model for imperative programs in a language of expressions. It provides a good integration of functional and imperative features in the language, allowing the programmer to use state where necessary without compromising the full expressiveness of a functional language.
Acknowledgments
Thanks to Joe Morris, Muy Thomas, and Campbell Fraser for reading drafts (on weekends) and suggesting improvements. Thanks to Satnam Singh for suggesting Bresenham's circle drawing algorithm - which brought us on to Bresenham's line drawing algorithm.
References
[Bac89] Roland Backhouse. An exploration of the Bird-Meertens Formalism. International Summerschool on Constructive Algorithmics, Ameland 1989, September 1989.
[Bir86] Richard S. Bird. An introduction to the theory of lists. In M. Broy, editor, Logic of Programming and Calculi of Discrete Design, volume F36 of NATO ASI Series. Springer Verlag, 1986. [Bre65] J. E. Bresenham. An algorithm for computer control of a digital plotter. IBM Syst. J., 4(1):25 { 30, 1965. [Lau93] John Launchbury. Lazy imperative programming. ACM SigPlan Workshop on State in Prog. Langs., June 1993. [LPJ94] John Launchbury and Simon Peyton Jones. Lazy functional state threads. In Programming Languages Design and Implementation, 1994. [Mee86] Lambert Meertens. Algorithmics - towards programming as a mathematical activity. Mathematics and Computer Science, 1, 1986. CWI Monographs (J. W. de Bakker, M. Hazewinkel, J. K. Lenstra, eds.) North Holland, Puhl. Co. [Mor89] J.M. Morris. Programs from Speci cations. In Edsger W. Dijkstra, editor, Formal Development of Programs and Proofs, University of Texas at Austin Year of Programming Series, chapter 9, pages 81{ 115. Addison-Wesley, 1989. [Mor90] C. Morgan. Programming from Speci cations. Prentice Hall, U.K., 1990. [O'D93] John T. O'Donnell. Bidirectional fold and scan. In John T. O'Donnell and Kevin Hammond, editors, Functional Programming, Glasgow 1993, Workshops in Computing Science, pages 193 { 200. Springer Verlag, July 1993. Proceedings of the 1993 Glasgow Workshop on Functional Programming, Ayr, Scotland, 5 - 7 July 1993. [PJW93] Simon L. Peyton Jones and Philip Wadler. Imperative functional programming. Principles of Programming Languages, January 1993. [Sne93] Jan L. A. Snepscheut. What computing is all about. Texts and Monographs in Computer Science. Springer Verlag, 1993.
[Spr82] Robert F. Sproull. Using program transformations to derive linedrawing algorithms. ACM Transactions on Graphics, 1(4):259 { 273, October 1982. [SS87] Rod Salmon and Mel Slater. Computer Graphics, Systems and Concepts. Addison Wesley, 1987. [Wad92] Philip Wadler. The essence of functional programming. January 1992. Presented at 19th Annual Symposium on Principles of Programming languages, Albuquerque, New Mexico.