<<

c

Kluwer Academic Publishers Boston Manufactured in The Netherlands

State in Haskell

JOHN LAUNCHBURY jlcseogiedu

Oregon Graduate Institute PO Box Portland OR

SIMON L PEYTON JONES simonp jdcsglasgowacuk

University of Glasgow G QQ Scotland

Abstract Some algorithms make critical internal use of up datable state even though their

external sp ecication is purely functional Based on earlier work on monads we present a way of

securely encapsulating stateful computations that manipulate multiple named mutable ob jects

in the context of a nonstrict purelyfunctional language The security of the encapsulation is

assured by the type system using parametricity The same framework is also used to handle

inputoutput op erations state changes on the external world and calls to C

Introduction

Purely languages allow many algorithms to b e expressed

very concisely but there are a few algorithms in which inplace up datable state

seems to play a crucial role For these algorithms purelyfunctional languages

which lack up datable state app ear to b e inherently inecient Ponder McGeer

Ng Examples of such algorithms include

Algorithms based on the use of incrementallymodied hash tables where lo okups

are interleaved with the insertion of new items

The unionnd algorithm which relies for its eciency on the set representa

tions b eing simplied each time the structure is examined

Many graph algorithms which require a dynamically changing structure in

which sharing is explicit so that changes are visible nonlo cally

There is furthermore one absolutely unavoidable use of state in every functional

program inputoutput The plain fact of the matter is that the whole purp ose of

running a program functional or otherwise is to make some change to the world

an up dateinplace if you please In many programs these IO eects are rather

complex involving interleaved reads from and writes to the world state

We use the term stateful to describ e computations or algorithms in which the

programmer really do es want to manipulate up datable state What has b een

lacking until now is a clean way of describing such algorithms in a functional lan

guage esp ecially a nonstrict one without throwing away the main virtues

of functional languages indep endence of order of evaluation the ChurchRosser prop erty nonstrict semantics and so on

2

In this pap er we describ e a way to express stateful algorithms in nonstrict purely

functional languages The approach is a development of our earlier work on monadic

IO and state encapsulation Launchbury Peyton Jones Wadler

but with an imp ortant technical innovation we use parametric p olymorphism to

achieve safe encapsulation of state It turns out that this allows mutable ob jects

to b e named without losing safety and it also allows inputoutput to b e smo othly

integrated with other state manipulation

The other imp ortant feature of this pap er is that it describ es a complete system

and one that is implemented in the Glasgow Haskell and freely available

The system has the following prop erties

Complete referential transparency is maintained At rst it is not clear what

this statement means how can a stateful computation b e said to b e referentially

transparent

To b e more precise a stateful computation is a state transformer that is a

from an initial state to a nal state It is like a script detailing the

actions to b e p erformed on its input state Like any other function it is quite

p ossible to apply a single stateful computation to more than one input state

and of course its b ehaviour may dep end on that state

But in addition we guarantee that the state is used in a singlethreaded way

Consequently the nal state can b e constructed by mo difying the input state

inplace This ecient implementation resp ects the purelyfunctional seman

tics of the statetransformer function so all the usual techniques for reasoning

ab out functional programs continue to work Similarly stateful programs can

b e exp osed to the full range of program transformations applied by a compiler

with no sp ecial cases or side conditions

The programmer has complete control over where inplace up dates are used and

where they are not For example there is no complex analysis to determine

when an array is used in a singlethreaded way Since the viability of the entire

program may b e predicated on the use of inplace up dates the programmer

must b e condent in and b e able to reason ab out the outcome

Mutable ob jects can b e named This ability sounds inno cuous enough but once

an ob ject can b e named its use cannot b e controlled as readily Yet naming is

imp ortant For example it gives us the ability to manipulate multiple mutable

ob jects simultaneously

Inputoutput takes its place as a sp ecialised form of stateful computation In

deed the type of IOp erforming computations is an instance of the more

p olymorphic type of stateful computations Along with IO comes the ability

to call imp erative pro cedures written in other languages

It is p ossible to encapsulate stateful computations so that they app ear to the

rest of the program as pure stateless functions which are guaranteed by the

3

type system to have no interactions whatever with other computations whether

stateful or otherwise except via the values of arguments and results of course

Complete safety is maintained by this encapsulation A program may contain

an arbitrary number of stateful subcomputations each simultaneously active

without concern that a mutable ob ject from one might b e mutated by another

Stateful computations can even b e p erformed lazily without losing safety For

example supp ose that stateful depthrst search of a graph returns a list of

vertices in depthrst order If the consumer of this list only evaluates the

rst few elements of the list then only enough of the stateful computation is

executed to pro duce those elements

Overview

This section introduces the key ideas of our approach to stateful computation We

b egin with the programmerseyeview

State transformers

A state transformer of type ST s a is a computation which transforms a state

indexed by type s and delivers a value of type a You can think of it as a pure

function taking a state as its argument and delivering a state and a value as its

result We depict a state transformer like this

Result

State in State out

From a semantic p oint of view this is a purelyfunctional account of state For

example b eing a pure function a state transformer is a rstclass value it can b e

passed to a function returned as a result stored in a data structure duplicated

freely and so on Of course it is our intention that the new state will actually

b e constructed by mo difying the old one in place a matter to which we return in

Section From now on we take the term state transformer to b e synonymous

with stateful computation the computation is seen as transforming one state

into another

A state transformer can have other inputs b esides the state if so it will have a

functional type It can also have many results by returning them in a tuple For

example a state transformer with two inputs of type Int and two results of type

Int and Bool would have the type

4

Int Int ST s IntBool Its picture might lo ok like this

Inputs Results

State in State out

The simplest state transformer returnST simply delivers a value without aecting

the state at all

returnST a ST s a

The picture for returnST is like this

State in State out

Mutable variables

Next we need to provide some primitive state transformers which op erate on the

state The simplest thing to provide is the ability to allo cate read and write

mutable variables

newVar a ST s MutVar s a

readVar MutVar s a ST s a

writeVar MutVar s a a ST s

The type MutVar s a is the type of references allo cated from a state indexed by

s and containing a value of type a A reference can b e thought of as the name of

or address of a variable an up datable lo cation in the state capable of holding a

value The state contains a nite mapping of references to values

Notice that unlike SMLs ref types for example MutVars are parameterised over

the type of the state as well as over the type of the value to which the reference

is mapp ed by the state a decision whose imp ortance will b ecome apparent in

Section We use the name MutVar for the type of references rather than Ref

sp ecically to avoid confusion with SML

Returning to the primitives the function newVar takes an initial value of type

a say and delivers a state transformer of type ST s MutVar s a When this

5

state transformer is applied to a state it allo cates a fresh reference that is one

currently not used in the state augments the state to map the new reference to

the supplied value and returns the reference along with the mo died state

Given a reference v readVar v is a state transformer which leaves the state

unchanged but uses the state to map the reference to its value

The function writeVar transforms the state so that it maps the given reference to

a new value Notice that the reference itself does not change it is the state which is

mo died writeVar delivers a result of the unit type a type which only has one

value apart from b ottom also written A state transformer of type ST s

is useful only for its eect on the state

Comp osing state transformers

State transformers can b e comp osed in sequence to form a larger state transformer

using thenST which has type

thenST ST s a a ST s b ST s b

The picture for s thenST s is like this

s1 s2

State in State out

Notice that the two computations must manipulate state indexed by the same type

s Notice also that thenST is inherently sequential b ecause the state consumed by

the second computation is that pro duced by the rst Indeed we often refer to a

state transformer as a thread invoking the picture of a series of primitive stateful

op erations threaded together by a state passed from one to the next

Putting together what we have so far here is a pro cedure which swaps the

contents of two variables

swap MutVar s a MutVar s a ST s

swap v w readVar v thenST a

readVar w thenST b

writeVar v b thenST

writeVar w a

When swap v w is executed in a state thread that is when applied to a state

v is dereference d returning a value which is b ound to a Similarly the value of w

is b ound to b New values are then written into the state at these lo cations these

values b eing b and a resp ectively

The syntax needs a little explanation The form ae is Haskells syntax for

a lambda abstraction The b o dy of the lambda abstraction e extends as far to

6

the right as p ossible So in the co de for swap the second argument of the rst

thenST extends all the way from the a to the end of the function That is just

as you would exp ect the second argument of a thenST is meant to b e a function

Furthermore the parentheses enclosing the lambda abstractions can b e omitted

and we will do so from now on They would only b e required if we wanted the

lambda abstraction to extend less far than the end of the expression Lastly the

in the secondlast line is a wildcard pattern which matches any value We use

it here b ecause the writeVar do es not return a value of interest

Other useful combinators

To avoid the frequent app earance of lambda abstractions with wildcard patterns

we provide a sp ecial form of thenST called thenST with the following denition

thenST ST s ST s b ST s b

thenST st st st thenST

st

Unlike thenST the second argument of thenST is not a function so the lambda

isnt required Using thenST and omitting parentheses we can rewrite swap more

tidily as follows

swap MutVar s a MutVar s a ST s

swap v w readVar v thenST a

readVar w thenST b

writeVar v b thenST

writeVar w a

Four other useful combinators denable in terms of thenST and returnST are

listST listST mapST and mapST

listST ST s a ST s a

listST sts foldr consST nilST sts

where

nilST returnST

consST m ms m thenST r

ms thenST rs

returnST rrs

listST ST s a ST s

listST sts foldr thenST returnST sts

mapST a ST s b a ST s b

mapST f xs listST map f xs

7

mapST a ST s a ST s

mapST f xs listST map f xs

The rst listST takes a list of state transformers each indexed by the same

state type and returning a value of the same type It glues them together into a

single state transformer which comp oses its comp onents together in sequence and

collects their results into a list The other three are useful variants mapST for

example is rather like the normal map function it applies a function rep eatedly

to the elements of a list but then it also feeds the state through the resulting state

transformers

Encapsulation

So far we have b een able to combine state transformers to make larger state trans

formers but how can we make a state transformer part of a larger program which

do es not manipulate state at all What we need is a function runST with a type

something like the following

runST ST s a a

The idea is that runST takes a state transformer as its argument conjures up

an initial empty state applies the state transformer to it and returns the result

while discarding the nal state The initial state is empty in the sense that no

references have b een allo cated in it by newVar it is the empty mapping Here is a

tiny example of runST in action

three Int

three runST newVar thenST v

writeVar v thenST

readVar v

This denition of the value three runs a state thread which allo cates a new vari

able writes into it reads the variable and returns the value thus read Sections

and gives some more convincing uses of runST

A aw

But there seems to b e a terrible aw what is to prevent a reference from one thread

b eing used in another For example

let v runST newVar True

in

runST readVar v

8

Here the reference allo cated in the rst runSTs thread is used inside the second

runST Doing so would b e a great mistake b ecause reads in one thread are not

sequenced with resp ect to writes in the other and hence the result of the program

would dep end on the evaluation order used to execute it It seems at rst that a

runtime check might b e required to ensure that references are only dereferenced in

the thread which allo cated them Unfortunately this would b e exp ensive

Even worse our exp erience suggests that it is surprisingly tricky to implement

such a runtime check The obvious idea is to allo cate a unique statethread identier

with each call to runST which is carried along in the state Every reference would

include the identier of the thread in which it was created and whenever a reference

was used for reading or writing a runtime check is made to ensure that the identier

in the reference matches that in the state

This sounds easy enough alb eit p erhaps inecient The trouble is that it do es

not work Consider the following recursive denition

foo runST newVar thenST v

writeVar foo thenST

returnST v

Do es this give a runtime error No the write is in the same thread as the allo

cate However the following pair of mutuallyrecursive denitions ought to b ehave

identically

foo runST newVar thenST v

writeVar foo thenST

returnST v

foo runST newVar thenST v

writeVar foo thenST

returnST v

All that we have done is to unfold the denition of foo once which certainly should

not change the semantics of the program But alas each write sees a variable

from the other thread so the runtime check will fail A p erfectly resp ectable

program transformation has changed the b ehaviour of the runtime check which is

unacceptable

The solution parametricity

This problem brings us to the main technical contribution of the pap er the dif

culties with runST can all b e solved by giving it a more sp ecic type The type

given for runST ab ove is implicitly universally quantied over b oth s and a If we

put in the quantication explicitly the type might b e written

runST sa ST s a a

A runST with this type could legally b e applied to any state transformer regardless

of the types to which s and a are instantiated However we can b e more precise

9

what we really want to say is that runST should only b e applied to a state trans

former which uses newVar to create any references which are used in that thread

To put it another way the argument of runST should not make any assumptions

ab out what has already b een allo cated in the initial state That is runST should

work regardless of what initial state it is given So the type of runST should b e

runST a s ST s a a

This is not a HindleyMilner type b ecause the quantiers are not all at the top

level it is an example of rank p olymorphism McCracken

Why do es this type prevent the capture of references from one thread into

another Consider our example again

let v runST newVar True

in

runST readVar v

In the last line a reference v is used in a stateful thread readVar v even though

the latter is supp osedly encapsulated by runST This is where the type checker

comes into its own During typechecking the type of readVar v will dep end on

the type of v so for example the type derivation will contain a judgement of the

form

f v MutVar s Boolg readVar v ST s Bool

Now in order to apply runST we have to b e able to generalise the type of readVar v

with resp ect to s but we cannot as s is free in the type environment readVar v

simply do es not have type sST s Bool

What ab out the other way round Lets check that the type of runST prevents

the escap e of references from a thread Consider the denition of v ab ove

v runST newVar True

Here v is a reference that is allo cated within the thread but then released to the

outside world Again consider what happ ens during typechecking The expression

newVar True has type ST s MutVar s Bool which will generalise nicely to

sST s MutVar s Bool However this still do es not match the type of runST

To see this consider the instance of runST with a instantiated to MutVar s Bool

runST s ST s MutVar s Bool MutVar s Bool

We have had to rename the b ound variable s in the type of runST to avoid it

erroneously capturing the s in the type MutVar s Bool The argument type now

do esnt match the type of newVar True Indeed there is no instance of runST

which can b e applied to newVar True

Just to demonstrate that the type of runST do es nevertheless p ermit one thread

to manipulate references b elonging to another thread here is a p erfectly legitimate alb eit articial example 10

runST runST

runST

Figure An informal picture of a program with state threads

f MutVar s a MutVar s a

f v runST newVar v thenST w

readVar w

where v is a reference from some other arbitrary state thread Because v is not

accessed its state type do es not aect the lo cal state type of the short thread

which is in fact totally p olymorphic in v Thus it is ne for an encapsulated state

thread to manipulate references from other threads so long as no attempt is made

to dereference them

In short by the exp edient of giving runST a rank p olymorphic type we can

enforce the safe encapsulation of state transformers More details on this are given

in Sections and where we show that runSTs type can b e accommo dated with

only a minor enhancement to the type checker

Summary

We have describ ed a small collection of

Primitive state transformers namely returnST newVar readVar and writeVar

Plumbing combinators which comp ose state transformers together namely

thenST and its derivatives thenST listST mapST and so on

An encapsulator runST which runs a state transformer on the empty state

discards the resulting state and returns the result delivered by the state trans

former

Figure gives an informal picture of a program with a number of calls to runST

Each call to runST gives rise to an indep endent thread depicted as a large oval

11

The plumbing combinators ensure that the state is singlethreaded so that the

net eect is to plumb the state through a chain of primitive op erations newVar

readVar and writeVar each of which is depicted as a small square b ox The

relative interleaving of the primitive op erations in each thread is undened but

since the threads share no state dierent interleavings cannot give rise to dierent

results

Notice that it is only the state that is singlethreaded Both references and state

transformers in contrast are rstclass values which can b e duplicated discarded

stored in data structures passed to functions returned as results and so on A

reference can only b e used however by bringing it back together with its state

in a readVar or writeVar op eration

The crucial idea is that of indexing state transformers states and mutable

variables with a type It is usual for a value of type Tree t say to contain

subcomp onents of type t That is not the case for mutable variables and state

transformers In the type ST s a the s is used to lab el the state transformer

and force compatibility b etween state transformers which are comp osed together

Similarly the s in MutVar s a simply ensures that a reference can only b e deref

erenced in its state thread It is for this reason that we sp eak of a state b eing

indexed by a type rather than having that type

The type constructor ST together with the functions returnST and thenST form a

socalled Moggi and have a nice algebra For a detailed discussion

of monads see Wadler a

Array references

So far we have introduced the idea of references Section which can b e thought

of as a single mutable b ox Sometimes though we want to up date an array which

should b e thought of as many b oxes each indep endently mutable For that we

provide op erations to allo cate read and write elements of arrays They have the

following types

newArr Ix i ii v ST s MutArr s i v

readArr Ix i MutArr s i v i ST s v

writeArr Ix i MutArr s i v i v ST s

Like references newArr allo cates a new array whose b ounds are given by its rst

argument The second argument is a value to which each lo cation is initialised The

state transformer returns a reference to the array of type MutArr s i v which we

call an array reference The functions readArr and writeArr do what their names

suggest The result is undened if the index is out of b ounds

The three op erations can b e implemented using the mutable variables already

introduced by representing an array reference as an immutable array of references thus

12

type MutArr s i v Array i MutVar s v

This denition makes use of the standard Haskell type Array i v the type of

arrays indexed by values of type i with elements of type v Haskell arrays are

constructed by the function array and indexed with the inx op erator whose

types are as follows

array Ix i ii iv Array i v

Ix i Array i v i v

The arrayconstruction function array takes the b ounds of the array and list of

indexvalue pairs and returns an array constructed by lling in the array in the

way sp ecied by the list of indexvalue pairs

The implementation of newArr readArr and writeArr is then straightforward

newArr bds init mapST newVar indices thenST vs

returnST array bds indices zip vs

where

indices iv

indices range bds

readArr arr i readVar arri

writeArr arr i v writeVar arri v

The only interesting denition is that for newArr which uses mapST Section to

apply newVar to each index The standard function range is applied to the b ounds

of the array that is a pair of values in class Ix to deliver a list of all the

indices of the array

In practice we do not implement mutable arrays in this way for two reasons

Implementing arrays in terms of variables is rather inecient A single mutable

array of elements would require an immutable array of elements plus

separatelyallo cated mutable variables Each read or write would require

two memory references one to the array and a second to the variable It would

obviously b e more sensible to allo cate one mutable array of elements and

have writeArr mutate the elements directly

Implementing arrays in terms of variables dep ends on the existence of standard

Haskell arrays How are the latter to b e implemented Presumably in a se

quential system the implementation of array will involve allo cating a suitable

blo ck of memory and running down the list of indexvalue pairs lling in the

sp ecied elements of the array as we go Lo oked at like this we need mutable

arrays to implement immutable arrays We return to this topic in Section

Because of these considerations we actually provide newArray readArr and writeArr

as primitives and dene newVar readVar and writeVar in terms of them by rep resenting variables as arrays of size one

13

Inputoutput

Now that we have the statetransformer framework in place we can give a new ac

count of inputoutput An IOp erforming computation is of type ST RealWorld a

that is it is a state transformer transforming a state of type RealWorld and de

livering a value of type a The only thing which makes it sp ecial is the type of the

state it transforms namely RealWorld an abstract type whose values represent the

real world It is convenient to use a type synonym to express this sp ecialisation

type IO a ST RealWorld a

Since IO a is an instance of ST s a it follows that all the statetransformer

primitives concerning references and arrays work equally well when mixed with IO

op erations More than that the same plumbing combinators thenST returnST

and so on work for IO as for other state transformers In addition however we

provide a variety of IO op erations that work only on the IO instance of state that

is they are not p olymorphic in the state such as

putChar Char IO

getChar IO Char

It is easy to build more sophisticated IO op erations on top of these For example

putString Char IO

putString returnST

putString ccs putChar c thenST

putString cs

or equivalently

putString cs mapST putChar cs

System calls

It would b e p ossible to provide putChar and getChar as primitives that is func

tions which are not denable in Haskell The diculty is that there are p otentially

a very large collection of such primitive IO op erations and it is very likely that

programmers will want to add new ones of their own To meet this concern we

provide just one primitive IO op eration called ccall which allows the Haskell

programmer to call any C pro cedure Using ccall we can dene all the other IO

op erations for example putChar is dened like this

putChar Char IO

14

putChar c ccall putchar c

That is the state transformer putChar c transforms the real world by calling

the C function putchar passing it the character c The value returned by the

call is ignored as indicated by the result type of putChar Similarly getChar is

implemented like this

getChar IO Char

getChar ccall getchar

We implement ccall as a new language construct rather than as an ordinary

function b ecause we want it to work regardless of the number and type of its

arguments An ordinary function p ossessing only one type would take a xed

number of arguments of xed type The restrictions placed on its use are

All the arguments and the result must b e types which C understands Int

Float Double Bool String or Array There is no automatic conversion of

more complex structured types such as lists apart from lists of characters

which are treated sp ecially as strings or trees Furthermore the types involved

must b e statically deducible by the compiler from the surrounding context that

is ccall cannot b e used in a p olymorphic context The programmer can easily

supply any missing type information with a type signature

The rst argument of ccall which is the name of the C function to b e called

must app ear literally It is part of the language construct

Running IO

The IO type is a particular instance of state transformers so in particular IO

op erations are not p olymorphic in the state An immediate consequence of this is

that IO operations cannot be encapsulated using runST Why not Again b ecause

of runSTs type It demands that its state transformer argument b e universally

quantied over the state but that is exactly what IO is not

Fortunately this is exactly what we want If IO op erations could b e encapsulated

then it would b e p ossible to write apparently pure functions but whose b ehaviour

dep ended on external factors the contents of a le user input a shared C variable

etc The language would no longer exhibit referential transparency

How then are IO op erations executed at all The meaning of the whole program

is given by the value of the toplevel identier mainIO

mainIO IO

mainIO is an IO state transformer which is applied to the external world state by

the op erating system Semantically sp eaking it returns a new world state and the

15

changes embo died therein are applied to the real world Of course as in the case of

mutable variables our intention is that the program will actually mutate the real

world in place

By this means it is p ossible to give a full denition of Haskells standard in

putoutput b ehaviour involving lists of requests and resp onses as well as much

more Indeed the Glasgow implementation of the Haskell IO system is itself now

written entirely in Haskell using ccall to invoke Unix IO primitives directly

The same techniques have b een used to write libraries of routines for calling the X

window system an imagepro cessing and so on

Some applications

In this section we give three applications of the state transformer framework and

assess its usefulness

A functional more

Our rst example concerns inputoutput The Unix more utility allows the user

to view the contents of a le a screenful at a time A status line is displayed to

give p ositional information and commands are provided to allow scrolling in either

direction

However more can only b e used to display ASCI I text A completely dierent

program would b e needed to scroll through a le of pictures or to scroll and render

a le of PostScript We can improve on this situation by separating the pro cess

into two

A representationspecic function which converts the ASCI I le description of

pictures PostScript or whatever to a list of IO actions each of which paints

a single screenful

asciiPages String IO

picturePages PictureDescriptions IO

postscriptPages String IO

A representationindependent function which takes a list of IO actions one

for each screenful and interacts with the user to navigate through them

more IO IO

Now the three viewers can b e built by comp osition

moreAscii more asciiPages

morePictures more picturePages

morePostscript more postscriptPages

16

Notice the complete separation of concerns The asciiPages function is concerned

only with displaying ASCI I text on the screen and not at all with user interaction

The more function is concerned only with interaction with the user and not at

all with the nature of the material b eing displayed This separation of concerns

is achieved by passing a list of IO actions from one function to another Notice

to o that some of these actions may b e p erformed more than once if for example

the user scrolls back to a previouslydisplayed page None of this is p ossible in any

imp erative language we know of

Our solution has its shortcomings The Unix more allows the user to scroll a single

line at a time whereas ours only allows scrolling in units of pages It is a common

discovery that go o d abstractions sometimes conict with arbitrary functionality

Depth rst search

Depthrst search is a key comp onent of many graph algorithms It is one of the

very few algorithms for which an ecient algorithm is most lucidly expressed using

mutable state so it makes a go o d application of the ideas presented in this pap er

Our depthrst search function dfs is given in Figure It takes a graph g and

a list of vertices vs and returns a list of trees or forest which collectively

span g Furthermore the forest has the depthrst prop erty that is no edge in

the original graph traverses the forest from left to right The list of vertices vs gives

an initial ordering for searching the vertices which is used to resume the search

whenever one is completed Clearly the head of vs will b e the ro ot of the very rst

tree

The graph is represented by an array indexed by vertices in which each element

contains a list of the vertices reachable directly from that vertex The dfs function

b egins by introducing a fresh state thread allo cating an array of marks initialised

to False and then calling the lo cally dened function search The whole thing is

encapsulated by runST

When searching a list of vertices the mark asso ciated with the rst vertex is

examined and if True the vertex is discarded and the rest are searched If however

the mark is False indicating that the vertex has not b een examined previously

then it is marked True and two recursive calls of search are p erformed each of

which returns a list of trees The rst call namely search marks gv is given

the edges leading from v and it pro duces a forest ts which is built into a tree

with v at the ro otall these no des are reachable from v The second recursive call

search marks vs pro duces a forest of those vertices not reachable from v and

not previously visited The tree ro oted at v is added to the front of this forest

giving the complete depthrst forest

In a nonstrict language an expression is evaluated in resp onse to demands from

the consumer of the expressions value This prop erty extends to values pro duced

by stateful computations In the case of depthrst search if only part of the forest

returned by dfs is evaluated then only part of the stateful computation will b e

carried out This is quite a remarkable prop erty we know of no other system

17

type Graph Array Vertex Vertex

data Tree a Node a Tree a

dfs Graph Vertex Tree Vertex

dfs g vs runST

newArr bounds g False thenST marks

search marks vs

where search MutArr s Vertex Bool Vertex

ST s Tree Vertex

search marks returnST

search marks vvs readArr marks v thenST visited

if visited then

search marks vs

else

writeArr marks v True thenST

search marks gv thenST ts

search marks vs thenST us

returnST Node v ts us

Figure Lazy depthrst search

which can execute a sequential imp erative algorithm incrementally in resp onse to

demands on its result value

This algorithm and many others derivable from it or denable in terms of it are

discussed in detail in King Launchbury

An interpreter

We conclude this section with a larger example of array references in use Figure

It denes an interpreter for a simple imp erative language whose input is the

program together with a list of input values and whose output is the list of values

written by the program The interpreter naturally involves a value representing the

state of the store The idea is of course that the store should b e implemented as

an inplaceup dated array and that is precisely what is achieved

The resulting program has the same laziness prop erty as our depthrst search

As successive elements of the result of a call to interpret are evaluated the in

terpreter will incrementally execute the program just far enough to get to the next

Write command when the returnST delivers a new element of the result list and

no further If only the rst few elements of the result are needed much of the imp erative program b eing interpreted will never b e executed at all

18

data Com Assign Var Exp Read Var Write Exp While Exp Com

type Var Char

data Exp

interpret Com Int Int

interpret cs input runST newArr AZ thenST store

newVar input thenST inp

command cs store inp

command Com MutArray s Int MutVar s Int ST s Int

command cs store inp obey cs

where

obey Com ST s Int

obey returnST

obey Assign v ecs eval e thenST val

writeArr store v val thenST

obey cs

obey Read vcs readVar inp thenST xxs

writeArr store v x thenST

writeVar inp xs thenST

obey cs

obey Write ecs eval e thenST out

obey cs thenST outs

returnST outouts

obey While e bscs eval e thenST val

if val then

obey cs

else

obey bs While e bs cs inp

eval Exp ST s Int

eval e

Figure An interpreter with lazy stream output

19

This example has long b een a classic test case for systems which infer single

threadedness Schmidt and is also used by Wadler in his pap er on monads

Wadler a The only unsatisfactory feature of the solution is that eval has

to b e written as a fullyedged state transformer while one might p erhaps like to

take advantage of its readonly nature

Summary

An obvious question arises when lo oking at monadic co de it app ears to dier only

sup ercially from an ordinary imp erative program Have we done any more than

discover a way to mimic C in Haskell

We b elieve that on the contrary there are very signicant dierences b etween

writing programs in C and writing in Haskell with monadic state transformers and

IO

Usually most of the program is neither stateful nor directly concerned with

IO The monadic approach allows the graceful coexistence of a small amount

of imp erative co de and the large purely functional part of the program

The imp erative comp onent of the program still enjoys all the b enets of higher

order functions p olymorphic typing and automaticallymanaged storage

A state transformer corresp onds more or less to a statement in C or Pascal

However a state transformer is a rstclass value which can b e stored in a

data structure passed to a function returned as a result and so on while

a C statement enjoys none of these prop erties This expressive p ower was

used in the more example ab ove Section where complete IO actions

are constructed by one function stored in a list and subsequently p erformed

p erhaps rep eatedly under the control of an entirely separate function

Imp erative languages provide a xed rep etoire of control structures conditional

statements while lo ops for lo ops and so on Because state transformers are

rst class values the programmer can dene functions which play the role of

applicationspecic control structures For example here is a function which

p erforms its argument a sp ecied number of times

repeatST Int ST s ST s

repeatST n st listST st i n

If we need a for lo op where the lo op index is used in the b o dy it is easily

provided

forST Int Int ST s ST s

forST n stf mapST stf n

20

And so on The p oint is not that these particular choices are the right ones

but rather that it is very easy to dene new ways to comp ose together small

state transformers to make larger ones

The usual coroutining b ehaviour of lazy evaluation in which the consumer of

a data structure coroutines with its pro ducer extends to stateful computation

as well As Hughes argues Hughes the ability to separate what is

computed from how much of it is computed is a p owerful aid to writing mo dular

programs

Formalism

Having given the programmers eye view it is time now to b e more formal and to

dene precisely the ideas we have discussed We have presented state transformers

in the context of the fullsized Haskell since that is where we

have implemented the ideas In order to give semantics to the constructs however

it is convenient to restrict ourselves to the essentials In particular we choose to

omit the sp ecial semantics of IO op erations with their calls to C Instead we fo cus

on providing a semantics for encapsulated state together with a pro of demonstrating

the security of encapsulation

A Language

We fo cus on lambda calculus extended with the state transformer op erations The

syntax of the language is given by

e x j k j e e j xe j

let x e in e j runST e

k thenST j returnST j

newVar j readVar j writeVar j

Type rules

The type rules are given in Figure and are the usual HindleyMilner rules except

that runST also requires a judgment of its own Treating it as a language construct

avoids the need to go b eyond HindleyMilner types So rather than actually give

runST the type

runST asST s a a

as suggested in the introduction we provide a typing judgement which has the same eect

21

e T T e T

AP P

e e T

x T e T

LAM

xe T T

e S x S e T

LE T

let x e in e T

V AR x S x S

e tS

SPEC t FV T

e S T t

e S

GE N t FV

e tS

e tST t T

RU N t FV T

runST e T

Figure Type rules

22

E E xpr Env Val

E k B k

E x x

E e e E e E e

E xe v E e fx v g

0 0

E let xe in e E e f ix fx E e g

B runST r unS T

B thenST thenS T

Figure Semantics of Terms

As usual we talk b oth of types and type schemes that is types p ossibly with

universal quantiers on the outside We use T for types S for type schemes and

K for type constants such as I nt and B ool

T t j K j T T j

ST T T j

MutVar T T

S T j tS

In addition ranges over type environments that is partial functions from term

variables to types and we write FV T for the free variables of type T and likewise

for type environments

Denotational Semantics

In Figure we extend a standard denotational semantics for a nonstrict lambda

calculus to include the semantics of state op erations by providing denitions for

the new constants

The valuation function E takes an expression and an environment and returns

a value We use Env for the domain of environments and Val for the domain of

values dened as follows

Q

v ar D Env

S

D Val

The environment maps a variable of type to a value in the domain D and

the domain of values is the union of all the D where ranges over monotypes

p olymorphic values lie in the intersection of their monomorphic instances

23

r etur nS T v v

0 0

thenS T m k k x where x m

if

new V ar v

p p v otherwise

where p dom

p dom

r eadV ar p

p otherwise

p dom

w r iteV ar p v

p v otherwise

r unS T m x where x m

Figure Semantics of State Combinators and Primitives

From the p oint of view of the language the type constructors ST and MutVar are

opaque To give them meaning however the semantics must provide them with

some structure

D S tate D S tate

a

ST s a

S tate N V al

?

D N

?

MutVar s a

A state transformer is a function which given a state pro duces a pair of results a

value and a new state The least dened state transformer is the function which

given any state returns the pair containing the b ottom value and the b ottom state

A state is a lifted nite partial function from lo cations represented by natural

numbers to values The b ottom state is totally undened This is the state that

results after an innite lo op We cannot tell even which variables exist let alone

what their values are Nonb ottom states are partial functions with welldened

domains which sp ecify which variables exist These variables may b e mapp ed to

any value including b ottom

References are denoted simply by natural numbers except that it is p ossible to

have an undened reference also denoted by The number represents a lo cation

in the state

It is worth noting in passing that the state parameter s is ignored in providing

semantic meaning It is purely a technical device which shows up in the pro ofs of

safety

The denitions of the state constants are given in Figure Neither returnST nor

thenST are strict in the state but b oth are single threaded Thus they sequentialise

state op erations and guarantee that only one copy of the state is required without themselves forcing op erations to b e p erformed

24

if

new V ar v

S

p p v otherwise

S

where p dom

p dom

r eadV ar q

S

q otherwise

S

p dom

w r iteV ar q v

S

q v otherwise

S

f N N j T y pe to g

D r an

S ?

MutVar s a

Figure Semantics of Indexed State Primitives

In contrast the primitive op erations are strict in the state In order to allo cate

a new variable for example we need to know which lo cations are free Similarly

with the other op erations in the semantics we take p dom to b e true if either

p or are b ottom This all has a direct op erational reading which we discuss in

Section

Finally the meaning of runST is given by applying its statetransforming argu

ment to the empty state and extracting the rst comp onent of the result Note

that runST is not strict in the nal stateit is simply discarded

Safety

In any program there may b e lots of concurrently active state threads which must

not b e allowed to interfere with one another That is state changes p erformed by

one thread must not aect the result of any other

We achieve this by using the type scheme for runST We use the parametric nature

of p olymorphism to show that values within a state thread including the nal

value cannot dep end on references generated by other state threads Section

contains the pro of of the key lemma Here we will fo cus on the overall reasoning

pro cess assuming that the lemma holds

Independence of threads

Figure contains a more complex version of the semantics of the state primitives

in which the references are resp ectively co ded and deco ded We assume an indexed

25

family of injections from the naturals to themselves to act as co dings This family

is indexed by types to allow the dierent instances of the primitives to use dierent

enco dings for the variables b ecause the co ding function is selected on the basis of

the particular instance type

When new V ar allo cates a new lo cation rather than return the address of the

lo cation it co des it using a particular co ding function When r eadV ar and

w r iteV ar come to dereference the variable they rst apply the inverse of the co ding

function to nd the true lo cation and then act as b efore

All the comp onent parts of the state thread are eventually joined together using

thenST whose type forces these various parts to have the same state instance and

hence use the same co ding function Similarly the fact that references carry the

type of their creator forces any dereferencer to b e in the same instance and also

use the same co ding function Intuitively therefore the b ehaviour of a state thread

should b e indep endent of the choice of co ding function Only if references managed

to cross state b oundaries or if the state information could somehow b ecome lost in

the pro cess of typechecking could something go wrong

The essence of the pro of is that nothing do es go wrong The key lemma states

that a state transformer which is p olymorphic in the state is indeed indep endent

of the co ding function used This means that state references do not cross state

b oundaries that is no reference is dereferenced in any thread other than its creator

If the converse were so then we could construct a state thread whose b ehaviour

was dependent in the particular co ding function used change the co ding function

so eectively changing what the external reference p oints to contradicting the

lemma

Finally as the choice of is irrelevant we are free to use the identity function

and disp ense with co dings completely This gives the original semantics of Figure

Key Lemma

The development here relies on parametric p olymorphism in the style of Mitchell

Meyer who detail what eect the addition of p olymorphic constants has

on parametricity In general we cannot hop e that adding new p olymorphic con

stants willynilly will leave parametricity intact In particular the addition of a

p olymorphic equality will have a disastrous eect on the level of parametricity

implied by a p olymorphic type

Mitchell and Meyer show that parametricity is preserved so long as each new

type constructor comes equipp ed with a corresp onding op eration on relations and

the new p olymorphic constants all satisfy the relevant logical relations implied

by their types

In our setting this means that we have to give an account of what State and

MutVar do to relations and show that newVar readVar and writeVar satisfy the

logical relations corresp onding to their types

Because we are here concerned purely with the issue of separating distinct state

threads we only fo cus on the p olymorphism with rep ect to the state parameter

26

Consequently we will assume that we only store values of some xed type X say in

the state and we completely sidestep the issue of proving that the retrieved values

are of the same type as the MutVar reference which p ointed to the lo cation

We dene

V ar s M utV ar a X

Thus Var is a unary type constructor which we now use instead of MutVar

Let R and S b e types and let r R S b e a relation b etween R and S Let

q b e a reference of type V ar R and q b e type V ar S We dene

R S

q V ar r q q q

R S R S

R S

S tate r

R S R S

Two variables are related if they p oint to the same lo cation and two states are

related if they are equal

Lemma

newVar readVar and writeVar are all logical relations

Pro of

We do the pro of in the case of newVar The others are just as easy From the type

of newVar we have to show that for all r R S

new V ar a V ar r S tatenew V ar a

R S

Expanding out the denition gives

p p p v p v

R S

R S

which is clearly true 2

As the constants are parametric so are terms built from them this is the force

of Mitchell and Meyers result Thus we deduce the key lemma as a corollary

Lemma

If m sS T s T where s FV T then for any types R and S and any state

we have

m m

R S

As it is the instance of m which selects its co ding for variables the theorem states

that the result of a p olymorphic state transformer is indep endent of its internal

co ding This is exactly what we needed to show for our earlier reasoning to b e

supp orted

Haskell Arrays

Next we turn our attention to the implementation of immutable Haskell arrays

We will show that the provision of mutable arrays Section provides an elegant

route to an ecient implementation of immutable arrays

27

Implementing array

As we have already indicated Haskells immutable arrays are constructed by ap

plying the function array to a list of indexvalue pairs For example here is an

array in which each element is the square of its index

array n iii i n

This expression makes use of a list comprehension iii i n

which should b e read the list of all pairs iii such that i is drawn from

the list n Now it is obviously a big waste to construct the intermediate

list of index value pairs It would b e much b etter to compile this expression into

a simple lo op which appropriately initialises each element of the array Unfortu

nately it is much less obvious how to achieve optimisation at least in a way which

is not brittle For example it would b e a pity if the optimisation was lost if the

expression was instead written in this equivalent form

array n map i iii n

An obvious idea is to try to make use of deforestation Deforestation is the generic

name used for transformations which aim to eliminate intermediate lists that is

lists used simply as glue b etween two parts of a functional program For example

in the expression

map f map g xs

there is an intermediate list map g xs which can usefully b e eliminated Quite a

bit of work has b een done on deforestation Chin Marlow Wadler

Wadler and our compiler includes deforestation as a standard transforma

tion Gill Launchbury Peyton Jones

The diculty with applying deforestation to the construction of arrays is this so

long as array is a primitive opaque operation there is nothing deforestation can

do because deforestation inherently consists of melding together part of the producer

of a list with part of its consumer The ip side is this if we can express array

in Haskell then our standard deforestation technique may b e able to eliminate the

intermediate list

With this motivation in mind we now give a denition of array using mutable

arrays

array Ix i ii iv Array i v

array bds ivs

runST

newArr bds unInit thenST arr

mapST fill arr ivs thenST

freezeArr arr

where

28

unInit error Uninitialised element

fill arr iv writeArr arr i v

The denition can b e read thus

The call to newArr allo cates a suitablysized blo ck of memory

The call to mapST p erforms in sequence the actions fill arr iv for

each iv in the indexvalue list Each of these actions lls in one element of

the array

The function freezeArray is a new primitive which converts the mutable array

into an immutable array

freezeArr Ix i MutArr s i v ST s Array i v

Op erationally sp eaking freezeArr takes the name of an array as its argument

lo oks it up in the state and returns a copy of what it nds along with the

unaltered state The copy is required in case a subsequent writeArr changes

the value of the array in the state but it is sometimes p ossible to avoid the

overhead of making the copy see Section

Finally the whole sequence is encapsulated in a runST Notice the use of encap

sulation here The implementation or internal details of array is imp erative

but its specication or external b ehaviour is purely functional Even the pres

ence of state cannot b e detected outside array

The imp ortant thing is that the list of indexvalue pairs ivs is now explicitly con

sumed by mapST which gives enough leverage for our deforestation transformation

to eliminate the intermediate list The details of the deforestation transformation

are given in Gill Launchbury Peyton Jones and are not germane here The

p oint is simply that exp osing the implementation of array to the transformation

system is the key step

Accumulating arrays

Of course we can also dene other Haskell array primitives in a similar fashion

For example accumArray is a standard Haskell array op eration with type

accumArray Ix i aba a ii

ib Array i a

The result of a call accumArray f x bnds ivs is an array whose size is deter

mined by bnds and whose values are dened by separating all the values in the list

ivs according to their index and then p erforming a leftfold op eration using f on

29

each collection starting with the value x Typical uses of accumArray might b e a

histogram for example

hist Ix i ii i Array i Int

hist bnds is accumArray bnds iiis inRange bnds i

which counts the o ccurrences of each element of the list is that falls within the

range given by the b ounds bnds Another example is bin sort

binSort Ix i ii ai a Array i a

binSort bnds key vs accumArray flip bnds key vvvvs

where the value in vs are placed in bins according to their key value as dened

by the function key whose results are assumed to lie in the range sp ecied by the

b ounds bnds Each bin that is each element of the array will contain a list

of the values with the same key value The lists start empty and new elements are

added using a version of cons in which the order of arguments is reversed In b oth

examples the array is built by a single pass along the input list

The implementation of accumArray simple and very similar to that of array

accumArray bnds f z ivs

runST

newArr bnds z thenST a

mapST update a ivs thenST

freezeArr a

where

update a iv readArr a i thenST x

writeArr a i f x v

If array and accumArray were primitive then the programmer would have no

recourse if he or she wanted some other arrayconstruction op erator Mutable

arrays allow the programmer to dene new array op erations without mo difying the

compiler or runtime system We describ e a more complex application of the same

idea in Section

Other useful combinators

We have found it useful to expand the range of combinators and primitives b eyond

the minimal set presented so far This section presents the ones we have found most useful

30

Equality

The references we have corresp ond very closely to p ointers to variables One

useful additional op eration on references is to determine whether two references are

aliases for the same variable so w r ites to the one will aect r eads from the other

It turns out to b e quite straightforward to add an additional constant eqMutVar

eqMutVar MutVar s a MutVar s a Bool

eqMutArr Ix i MutArr s i v MutArr s i v Bool

Notice that the result do es not dep end on the stateit is simply a b o olean Notice

also that we only provide a test on references which exist in the same state thread

References from dierent state threads cannot b e aliases for one another

Fixp oint

In lazy functional programs it is often useful to write recursive denitions such as

newTree min f tree min

Here part of the result of f is passed into f A standard example is a function

which replaces all the leaves of a tree with the minimum of all the leaves in a single

pass of the tree hence our choice of names in the example Bird The alert

reader may have noticed that it is imp ossible to write programs in this way if f is

a state transformer We might try

f tree min thenST newTree min

but alas the min result is not in scop e at the call What is needed is a new combi

nator fixST with type

fixST a ST s a ST s a and the usual knottying semantics which we depict thus

s

State in State out

Now we can write our recursive state transformer

fixST newTree min f tree min

The in this example sp ecies that the tuple should b e matched lazily If the

argument to fixST is a strict function then of course the result of the fixST call is b ottom

31

Implementation

The whole p oint of expressing stateful computations in the framework that we

have describ ed is that op erations which mo dify the state can up date the state

in place The implementation is therefore crucial to the whole enterprise rather

than b eing a p eripheral issue This section fo cuses on implementation issues and

app eals to some intuitions ab out what will generate go o d co de and what will not

Readers interested in a more substantial treatment of such intuitions are referred

to Peyton Jones Peyton Jones

We have in mind the following framework

The state of each encapsulated state thread is represented by a collection of

ob jects in heapallo cated storage

A reference is represented by the address of an ob ject in heapallo cated store

A read operation returns the current contents of the ob ject whose reference is

given

A write operation overwrites the contents of the sp ecied ob ject or in the case

of mutable arrays part of the contents

As the previous section outlined the correctness of this implementation relies to

tally on the type system Such a reliance is quite familiar for example the im

plementation of addition makes no attempt to check that its arguments are indeed

integers b ecause the type system ensures it In the same way the implementation

of state transformers makes no attempt to ensure for example that references are

only used in the same state thread in which they were created the type system

ensures that this is so

Up date in place

The most critical correctness issue concerns the up dateinplace b ehaviour of write

op erations Why is up dateinplace safe It is safe b ecause all the combinators

thenST returnST fixST use the state only in a singlethreaded manner Schmidt

that is they neither duplicate nor discard it Figure Furthermore all

the primitive op erations are strict in the state

It follows that a write op eration can mo dify the state in place b ecause a it has

the only copy of the incoming state and b since it is strict in the incoming state

and the preceding op eration will not pro duce its result state until it has computed

its result there can b e no other asyetunexecuted op erations p ending on that state

Can the programmer somehow duplicate the state No since the ST type is

opaque the only way the programmer can manipulate the state is via the com

binators thenST and returnST and fixST The programmer certainly do es have

access to named references into the state However it is p erfectly OK for these to

32

b e duplicated stored in data structures and so on Variables are immutable it is

only the state to which they refer that is altered by a write op eration

We nd these arguments convincing but they are certainly not formal A formal

pro of would necessarily involve some op erational semantics and a pro of that no

evaluation order could change the b ehaviour of the program We have not yet

undertaken such a pro of

Typechecking runST

Since runST has a rank type we needed to mo dify the type checker to include the

extra rule RU N in Figure The mo dication is quite straightforward b ecause

the rule for RU N is so similar to that for LE T All that is required is to check that

the type inferred for the argument of runST has a type of the form ST s where s

is an unconstrained type variable not app earing in

The RU N rule describ es how to typechecking applications of runST What of

other o ccurrences of runST For example the RU N rule do es not say how to type

the following expression in which runST app ears as an argument

map runST xs

We sidestep this diculty by insisting that runST only app ears applied to an

argument as implied by the syntax in Section It might b e p ossible to allow

greater generality but p erforming type inference in the presence of unrestricted

rank types is a much harder prop osition and one which is not necessary for our

enterprise Kfoury and Kfoury Wells explore this territory in detail

Eciency considerations

It would b e p ossible to implement state transformers by providing the combinators

thenST returnST etc and op erations readVar writeVar etc as primitives

But this would imp ose a very heavy overhead on each op eration and worse still on

comp osition For example a use of thenST would entail the construction in the

heap of two functionvalued arguments followed by a pro cedure call to thenST This

compares very p o orly with simple juxtap osition of co de which is how sequential

comp osition is implemented in conventional languages

We might attempt to recover some of this lost eciency by treating statetransformer

op erations sp ecially in the co de generator but that risks complicating an already

complex part of the compiler Instead we implement state transformers in a way

which is b oth direct and ecient we simply give Haskell denitions for the state

transformer type and its combinators These denitions are almost precise translit

erations of the semantics given for them in Figure

33

The state transformer implementation

A state transformer of type ST s a is represented by a function from State s to

a pair of the result of type a and the transformed state

type ST s a State s a State s

This representation of ST is not of course exp osed to the programmer lest he or

she write functions which duplicate or discard the state The one resp ect in which

this implementation diers from the semantics in Figure is in the result type of

ST the semantics used a simple pro duct whereas a Haskell pair is a lifted pro duct

That is the value diers from As we shall see this distinction has an

unfortunate implication for eciency

The denitions of thenST returnST and fixST follow immediately

returnST x s xs

thenST m k s k x s where xs m s

fixST k s rs where rs k r s

The b eauty of this approach is that all the combinators can then b e inlined

at their call sites thus largely removing the plumbing costs For example the

expression

m thenST v

m thenST v

returnST e

b ecomes after inlining thenST and returnST the much more ecient expression

s let vs m s

vs m s

in es

We have not so far given the implementation of runST which is intriguing

runST m r where rs m dummyState

Since its argument m works regardless of what state is passed to it we simply

pass a value representing the current state of the world As we will see shortly Sec

tion this value is never actually lo oked at so a constant value dummyState

will do We need to take care here though Consider the following expression

which has two distinct state threads

runST newVar thenST v e

runST newVar thenST v e

34

After inlining runST and thenST we transform to

let vs newVar dummyState in e s

let vs newVar dummyState in e s

Now it lo ok as though it would legitimate to share newVar dummyState as a

common subexpression

let vs newVar dummyState

in

e s

e s

but of course this transformation is b ogus The two dummyStates are distinct values

There are two solutions to this problem do not inline runST or alternatively

when inlining runST create a new constant dummyState on each o ccasion akin to

skolemization of logic variables

This provides us with a second reason why runST should not b e regarded as a

standard value its type provided the rst Rather runST is an eliminable language

construct

The co de generator must of course remain resp onsible for pro ducing the appro

priate co de for each primitive op eration such as readVar ccall and so on In our

implementation we actually provide a Haskell wrapp er for each primitive which

makes explicit the evaluation of their arguments using socalled unboxed values

Both the motivation for and the implementation of our approach to unboxed values

is detailed in Peyton Jones Launchbury and we do not rehearse it here

Strictness

In the previous section we pro duced the following co de from a comp osition of m

and m

s let vs m s

vs m s

in es

This might b e b etter than the original in which functionvalued arguments are

passed to thenST but it is still not very go o d In particular heapallo cated thunks

are created for m s and m s along with heapallo cated selectors to extract

the comp onents v s and v s resp ectively However the program is now

exp osed to the full range of analyses and program transformations implemented by

the compiler If the compiler can sp ot that the ab ove co de will b e used in a context

which is strict in either comp onent of the result tuple it will b e transformed to

s case m s of

35

vs case m s of

vs es

This is much more ecient First m is called returning a pair which is taken

apart then m is called and its result taken apart b efore returning the nal result

In our implementation no heap allo cation is p erformed at all If m and m are

primitive op erations then the co de implementing m simply follows that for m

just as it would in C

It turns out that even relatively simple strictness analysis can often successfully

enable this transformation provided that Haskell is extended in a mo dest but

imp ortant regard Consider the function

f MutVar s Int ST s

f x writeVar y thenST

returnST

Is f strict in its input state Intuitively it must b e its only useful result is its

result state which dep ends on its input state This argument holds for any value

of type ST s Do es the strictness analyser sp ot this strictness Alas it do es

not After inlining f b ecomes

f x s let s writeVar y s

in

s

This denition is manifestly not strict in either argument it will return a pair

regardless of the values of its arguments There are two problems with a common

cause

Haskell pairs are lifted so that is distinct from The semantics in

Figure used an ordinary unlifted pro duct so the lifting is not certainly not

required by semantics

The unit type is also a lifted domain with two values and When we

chose as the result type of state transfomers which had no result we certainly

did not have in mind that two distinct values could b e returned Again the

lifting of the Haskell type is not required

If neither pairs nor the unit type were lifted then it is easy to see that f is strict in

s Supp ose s were then b ecause the primitive writeVar is strict s would also

b e so the result of f would b e the pair But if were unlifted to o this

is the same as which if pairs are unlifted is the same as

In short in order to give the strictness analyser a real chance a state transformer

must return an unlifted pair and the result type of a state transformer which is

used only for its eect on the state should b e an unlifted onep oint type Though

this is an imp ortant eect it is far from obvious indeed it only b ecame clear to us as we were working on the nal version of this pap er

36

It is also regrettable that an o ccasionallysubstantial p erformance eect should

dep end on something as complex as strictness analysis Indeed we provide a variant

of returnST called returnStrictlyST which is strict in the state precisely to

allow a programmer to enforce strictness and hence ensure greater eciency Of

course if returnStrictlyST is used indiscriminately then the incremental laziness

of stateful computations discussed in Section is lost

In the sp ecial but common case of IO state transformers we can guarantee

to compile ecient co de b ecause the nal state of the IO thread wil l certainly

be demanded Why Because the whole p oint in running the program in the rst

place is to cause some change to the real world It is easy to use this strictness

prop erty which cannot of course b e inferred by the strictness analyser to ensure

that every IO state transformer is compiled eciently

Passing the state around

The implementation of the ST type given ab ove passes around an explicit state

Yet we said earlier that statemanipulating op erations are implemented by p er

forming state changes in the common global heap What then is the role of

the explicit state value which is passed around by the ab ove co de It plays two

imp ortant roles

Firstly the compiler shakes the co de around quite considerably is it p ossible

that it might somehow end up changing the order in which the primitive op era

tions are p erformed No it is not The input state of each primitive op eration is

pro duced by the preceding op eration so the ordering b etween them is maintained

by simple data dep endencies of the explicit state which are certainly preserved by

every correct program transformation

Secondly the explicit state allows us to express to the compiler the strictness of

the primitive op erations in the state The State type is dened like this

data State s MkState State s

That is a state is represented by a singleconstructor algebraic whose

only contents is a value of type State s the nally primitive type of states

The lifting implied by the MkState constructor corresp onds exactly to the lifting

in the semantics Using this denition of State we can now dene newVar for

example like this

newVar init MkState s case newVar init s of

vt v MkState t

This denition makes absolutely explicit the evaluation of the strictness of newVar

in its state argument nally calling the truly primitive newVar to p erform the allo cation

37

We think of a primitive state that is a value of type State s for some type

s as a token which stands for the state of the heap and in the case of the

IO thread the real world The implementation never actually insp ects a primitive

state value but it is faithfully passed to and returned from every primitive state

transformer op eration By the time the program reaches the co de generator the

role of these state values is over and the co de generator arranges to generate no

co de at all to move around values of type State

Arrays

The implementation of arrays is straightforward The only complication lies with

freezeArray which takes a mutable array and returns a frozen immutable copy

Often though we want to construct an array incrementally and then freeze it

p erforming no further mutation on the mutable array In this case it seems rather

a waste to copy the entire array only to discard the mutable version immediately

thereafter

The right solution is to do a go o d enough job in the compiler to sp ot this sp ecial

case What we actually do at the moment is to provide a highly dangerous op eration

unsafeFreezeArray whose type is the same as freezeArray but which works

without copying the mutable array Frankly this is a hack but since we only

exp ect to use it in one or two critical pieces of the standard library we couldnt

work up enough steam to do the job prop erly just to handle these few o ccasions

We do not provide general access to unsafeFreezeArray

Interleaved state transformers

The statetransformer comp osition combinator dened so far thenST is completely

sequential the state is passed from the rst state transformer on to the second

But sometimes that is not what is wanted Consider for example the op eration

of reading a le We may not want to sp ecify the precise relative ordering of

the individual characterbycharacter reads from the le and other IO op erations

Rather we may want the le to b e read lazily as its contents is demanded

We can provide this ability with a new combinator interleaveST

interleaveST ST s a ST s a

interleaveST m s rs where rs m s

Unlike every other state transformer so far interleaveST actually duplicates the

state The plumbing diagram for interleaveST s is like this 38

Result

s

State in State out

The semantics of interleaveST are much less easy to dene and reason ab out than

the semantics of our other combinators given that our intended implementation

remains that of up date in place The purp ose of this section is to provide several

motivating examples for interleaveST while leaving op en the question of how to

reason ab out whether it is b eing used safely In this context we regard a use of

interleaveST as safe if the resulting program can still b e evaluated in any order

that resp ects data dep endencies

Lazy le read

One way for interleaveST to b e safe is to regard it as splitting the state into two

disjoint parts In the lazyleread example the state of the le is passed into one

branch and the rest of the state of the world is passed into the other Since these

states are disjoint an arbitrary interleaving of op erations in each branch of the fork

is legitimate

Here is an implementation of lazy le read using interleaveST

readFile String IO Char

readFile filename openFile filename thenST f

interleaveST readCts f

readCts FileDescriptor IO Char

readCts f readCh f thenST c

if c eofChar

then returnST

else readCts f thenST cs

returnST ccs

Notice that the recursive call to readCts do es not immediately read the rest of the

le Because thenST and the following returnST are nonstrict the list ccs will

b e returned without further ado If cs is ever evaluated the recursive readCts will

then and only then b e p erformed This is a go o d example of laziness in action

Even though op erations are b eing rigidly sequenced in this case the reads of

successive characters of the le the rate at which this sequence is p erformed is

driven entirely by lazy evaluation The single call to interleaveST simply allows

these op erations to o ccur asynchronously with resp ect to other IO op erations on the main trunk

39

Uniquesupply trees

In the lazy leread example the use of interleaveST could b e regarded as split

ting the state into two disjoint parts However we have found some comp elling

situations in which the forkedo thread quite delib erately shares state with the

trunk This section explores the rst such example

A common problem in functional programs is to distribute a supply of unique

names around a program For example a compiler may want to give a unique

name to each identier in the program b eing compiled In an imp erative program

one might simply call GenSym for each identier to allo cate a unique name from

a global supply and to sideeect the supply so that subsequent calls to GenSym

will deliver a new value

In a functional program matters are not so simple indeed several pap ers have

discussed the problem Augustsson Rittri Synek Hanco ck Wadler

a The rest of this section shows a rather elegant implementation of the b est

approach that of Augustsson Rittri Synek The idea is to implement a

pair of abstract data types UniqueSupply and Unique with the following signature

newUniqueSupply IO UniqueSupply

splitUniqueSupply UniqueSupply UniqueSupply UniqueSupply

getUnique UniqueSupply Unique

instance Eq Unique

instance Ord Unique

instance Text Unique

The three instance declarations say that the Unique type has dened on it equality

and ordering op erations and mappings to and from strings Naturally the idea is

that the two UniqueSupplys returned by splitUniqueSupply are forever separate

and can never deliver the same Unique The implementation is given some extra

freedom by making newUniqueSupply into an IO op eration Dierent runs of the

same program are therefore p ermitted to allo cate uniques in a dierent order all

that matters ab out Uniques is that they are distinct from each other

One p ossible implementation would represent a UniqueSupply and a Unique by a

p otentially very long bitstring The splitUnique function would split a supply

into two by app ending a zero and a one to it resp ectively The trouble is of course

that the name supply is used very sparsely

The idea suggested by Augustsson Rittri Synek is to represent a UniqueSupply

by an innite tree which has a Unique at every no de and two child UniqueSupplys

data UniqueSupply US Unique UniqueSupply UniqueSupply

Now the implementation of splitUniqueSupply and getUnique are trivial and all

the excitement is in newUniqueSupply Here is its denition assuming for the sake

of simplicity that a Unique is represented by an Int

40

type Unique Int

newUniqueSupply IO UniqueSupply

newUniqueSupply

newVar thenST uvar

let

next IO Unique

next interleaveST

readVar uvar thenST u

writeVar uvar u thenST

returnStrictlyST u

supply IO UniqueSupply

supply interleaveST

next thenST u

supply thenST s

supply thenST s

returnST US u s s

in

supply

The two uses of interleaveST sp ecify that the relative ordering of the state changes

in next and in the two uses of supply is delib erately left to the implementation

The only side eects are in next which allo cates a new Unique so what this

amounts to is that the uniques are allo cated in the order in which the Uniques are

evaluated which is just what we wanted

If the program was recompiled with say a dierent analysis technique which

meant that the evaluation order changed then indeed dierent uniques would b e

generated But since the whole mkUniqueSupply op eration is typed as an IO

op eration there is no reason to suppose that the same uniques wil l be generated a

nice touch

There is one imp ortant subtlety in the co de namely the use of returnStrictlyST

in the denition of next The trap into which we fell headlong is this since

interleaveST discards the nal state and the result of the writeVar is also dis

carded if returnST is used instead there is nothing to force the writeVar to take

place at al l Indeed if we used the standard returnST no writeVars would b e

p erformed and each readVar would see the same undisturb ed value Of course

what we want to happ en is that once the thread in nexts righthandside is started

then it must run to completion That is exactly what returnStrictlyST achieves

The dierence b etween returnST and returnStrictlyST is simply that the lat

ter is strict in the state Op erationally it will not deliver a result at all until it

has evaluated its input state We can picture it like this compare the picture for

returnST in Section 41

State in State out

In this picture the result is held up by the valve until the state is available

Despite this subtlety this co de is a distinct improvement on that given in Au

gustsson Rittri Synek which has comments such as The gensym function

must b e co ded in assembler and A to oclever compiler might recognise the re

p eated calls to gen x and generate co de which creates sharingif such compiler

optimisations cannot b e turned o or fo oled one must generate co de for gen as

well as gensym by hand

Lazy arrays

A second example of the use of interleaveST where the forkedo thread shares

state with the trunk concerns socalled lazy arrays Haskell arrays are strict in the

list of indexvalue pairs and in the indices in this list though not in the values

An alternative and more p owerful array constructor would have the same type as

array but b e nonstrict in the indexvalue pairs

lazyArray Ix i ii iv Array i v

What do es it mean for lazyArray to b e nonstrict in the indexvalue pairs Pre

sumably it must return an array immediately and search the list only when the

array is indexed Since checking for duplicate indices would entail searching the

whole list lazyArray simply returns the value in the rst indexvalue pair in the

list with the sp ecied index In short the semantics of indexing a lazy array is

precisely that of searching an asso ciation list except that of course we hop e

that it will b e more ecient Figure gives a rather amazing program which uses

lazy arrays to compute prime numbers using the sieve of Eratosthenes

The basis of the algorithm is an array minFactor which for each index stores the

minimum factor of that index A number is prime therefore if the value stored

at its index in minFactor is equal to the number itself The function multiples

generates one or more multiples of its argument each paired with its argument

by rst returning its argument and then if the argument is prime returning more

multiples Note that multiple always returns its argument as a multiple before

checking its primality so guaranteeing that minFactor will have b een initialised if

not by this call of multiples then by a previous This prevents the program from

entering a black hole deadlo ck

How can we implement lazyArray The eect we want to achieve is

lazyArray allo cates and initialises a suitable array and returns it immediately without lo oking at the indexvalue pairs

42

primesUpTo Int Int

primesUpTo n filter isPrime n

where

minFactor Array Int Int

minFactor lazyArray n concat map multiples n

isPrime p Int Bool

isPrime p minFactorp p

multiples Int IntInt

multiples k kk if isPrime k

then mk m kkn

else

Figure Computing primes using the Sieve of Eratosthenes using a lazy array

When this array is indexed with the standard op erator the indexvalue list

is searched for the sp ecied index As a side eect of this search the array is

lled in with all the indexvalue pairs encountered up to and including the index

sought When this index is found the search terminates and the corresp onding

value is returned

If the array is subsequently indexed at the same index the value is returned

immediately

This b ehaviour is not easy to achieve in Haskell It relies inherently on imp erative

actions The fact that the results are indep endent of the order in which array

elements are accessed is a deep prop erty of the pro cess Nevertheless we can

express it all with the help of interleaveST the co de is given in Figure

Referring rst to Figure lazyArray allo cates the following mutable values

A variable feederVar to hold the feederlist of indexvalue pairs

A mutable array valArr in which the result of the whole call to lazyArray is

accumulated

A mutable array of b o oleans doneArr whose purp ose is to record when the cor

resp onding slot of valArr has b een assigned with its nal value Each element

of the doneArr is initialise to False

Next lazyArray initialises each slot in valArr by calling initVal dened in

the let Finally lazyArray freezes the array and returns the frozen value Here

we must use unsafeFreezeArray b ecause the whole idea is that valArr is go

ing to b e mutated as a side eect of the evaluation of its elements Recall that

43

lazyArray Ix i ii ival Array i val

lazyArray bounds feederList

runST

newVar feederList thenST feederVar

newArray bounds False thenST doneArr

newArray bounds initial thenST valArr

let

initVal k interleaveST

readVar feederVar thenST ivs

writeVar feederVar badVal thenST

fillUntil k ivs thenST ivs

writeVar feederVar ivs thenST

readArray valArr ix

thenST delayedVal

writeArray valArr k delayedVal

fillUntil k noValue k

fillUntil k iv ivs

readArray doneArr i thenST done

if not done then

writeArray doneArr i True thenST

writeArray valArr i v

else

returnST thenST

if i k then

returnST ivs

else

fillUntil k ivs

in

mapST initVal range bounds thenST

unsafeFreezeArr valArr

where

initial error lazyArray uninitialised element

badVal error lazyArray an index depends on a later value

noValue k error lazyArray no value for show index bounds k

Figure An implementation of lazy arrays 44

done_arr True False True False True 1 2 3 4 5 val_arr

610 18 initVal 2 initVal 4

f_list_var

(2,10) (4,7)

Figure Lazy array construction

unsafeFreezeArray converts a mutable array to a value of the immutablearray

type without copying the array further mutations of the mutable array will there

fore also aect the immutable value

Referring now to the lo cal functions the function initVal initialises the slot of

valArr with an interleaved state transformer which when its result is demanded

will

read feederVar to get the current list of asyetunconsumed indexvalue pairs

call fillUntil to consume the list of indexvalue pairs writing each value to

the appropriate slot of valArr the array done records whether that element

has already b een written until the soughtfor index is found

write the depleted list of indexvalue pairs back to feederVar

read the array to deliver the desired value

Further accesses to the same array slot will now nd the nal value rather than

the interleaved state transformer All of this is illustrated by Figure which

illustrates an intermediate state of a lazyarray value The array has ve slots of

which three have already b een lled in The remaining two are sp ecied by the

depleted p ortion of the list of indexvalue pairs contained in feederVar

45

Parallel state transformers

A careful reading of newUniqueSupply or lazyArray reveals an imp ortant resp ect

in which b oth are unsafe in the sense of indep endence of evaluation order Both

rely on reading a variable and writing back a mo died value in the case of

newUniqueSupply the variable is called uvar while in lazyArray it is feederVar

Implicitly we have assumed that if the read takes place then so will the write

and so they will in any sequential normalorder implementation However if two

threads are executed simultaneously which is one legitimate execution order

then this assumption might not hold and the programs would fail

This is not just a purists ob jection b ecause an obvious development is a variant

interleaveST which starts a concurrent pro cess to execute the forkedo thread

Such a variant which we call forkST is very useful For example in a graphical

IO system it might b e used to start a concurrent pro cess to handle IO in a new

p opup window indep endent of and concurrent with other IO op erations

In eect interleaveST forces us to address the usual textb o ok problems of

mutual exclusion and synchronisation that must b e solved by any system supp orting

b oth concurrency and shared state We are by no means the rst to meet these

issues in a functional setting Concurrent ML Reppy and the Istructures

Arvind Nikhil Pingali and Mstructures Barth Nikhil Arvind

of Id Nikhil are obvious examples We are currently studying how to

incorp orate some of these nowstandard solutions in our framework

Summary

It should b e clear by now that interleaveST has very undesirable prop erties It

duplicates and discards the state which gives rise to a very subtle class of pro

gramming errors We have so far failed to develop go o d techniques for reasoning

ab out its correctness At rst we wondered ab out ways to ensure that the two

state threads use dierent variables but two of our most interesting applications

lazyArray and newUniqueSupply delib erately p erform side eects on shared state

Their correctness dep ends on relatively deep metareasoning and a certain sort

of atomicity for example the read and write of uvar must take place atomically

in next in newUniquesupply

Should we outlaw interleaveST on the grounds that it is insuciently well b e

haved Not necessarily Outlawing interleaveST would simply drive its function

alitly underground rather than prevent it happ ening For example we want to

have lazy le reading If it cannot b e implemented in Haskell then it will have to

b e implemented underground as a primitive op eration written in C or machine

co de The same go es for uniquesupply trees and lazy arrays

Implementing such operations in C does not make them more hygienic or easy

to reason about On the contrary it is much easier to understand mo dify and

construct variants of them if they are implemented in a Haskell library mo dule than if they are embedded in the runtime system

46

The need for sp ecial care to b e taken is agged by the use of interleaveST

which identies a pro of obligation for the programmer to show that the results of

the program are nevertheless indep endent of evaluation order We fear that there

may b e no absolutely secure system that is one which guarantees the Church

Rosser prop erty which is also expressive enough to describ e the programs which

systems programmers at least want to write such as those ab ove We do however

regard interleaveST as useful primarily for systems programmers

Related work

Monads were introduced to by Moggi in the context of writing

mo dular language semantics Moggi He noticed that denotational seman

tics of languages could b e factored into two parts what happ ens to values and the

underlying computational mo del Furthermore all the usual computational mo d

els satised the categorical denition of monads often called triples in

theory including state exceptions jumps and so on Wadler subsequently wrote

several highly accessible pap ers showing how monads could b e similarly used as

a programming style in conventional functional programming languages Wadler

a Wadler b Wadler

Based on this work we developed a monadic approach to IO Peyton Jones

Wadler and state Launchbury in the context of nonstrict purely

functional languages The approach taken by these pap ers has two ma jor short

comings

State and inputoutput existed in separate frameworks The same general ap

proach can handle b oth but for example dierent combinators were required

to comp ose stateful computations from those required for IOp erforming com

putation

State could only safely b e handled if it was anonymous Consequently it was

dicult to write programs which manipulate more than one piece of state

at once Hence programs b ecame rather brittle an apparently inno cuous

change adding an extra up datable array b ecame dicult or imp ossible

Both these shortcomings are solved in this pap er the core of which app eared earlier

Launchbury Peyton Jones

Several other languages from the functional stable provide some kind of state For

example Standard ML provides reference types which may b e up dated Milner

Tofte The resulting system has serious shortcomings though The meaning

of programs which use references dep ends on a complete sp ecication of the order

of evaluation of the program Since SML is strict this is an acceptable price to pay

but it would b ecome unworkable in a nonstrict language where the exact order

of evaluation is hard to gure out What is worse however is that referential

transparency is lost Because an arbitrary function may rely on state accesses its

result need not dep end purely on the values of its arguments This has additional

47

implications for p olymorphism leading to a weakened form in order to maintain

type safety Tofte We have none of these problems here

The dataow language Id provides Istructures and Mstructures as mutable

datatypes Nikhil Within a stateful program referential transparency is

lost For Istructures the result is indep endent of evaluation order provided that

all subexpressions are eventually evaluated in case they sideeect an Istructure

For Mstructures the result of a program can dep end on evaluation order Com

pared with Istructures and Mstructures our approach p ermits lazy evaluation

where values are evaluated on demand and may never b e evaluated if they are

not required and supp orts a much stronger notion of encapsulation The big ad

vantage of Istructures and Mstructures is that they are b etter suited to parallel

programming than is our metho d

The Clean language takes a dierent approach Barendsen Smetsers

The Clean type system supp orts a form of linear types called unique types A

value whose type is unique can safely b e up dated in place b ecause the type system

ensures that the up dating op eration has the sole reference to the value The contrast

with our work is interesting We separate references from the state to which they

refer and do not p ermit explicit manipulation of the state Clean identies the

two and in consequence requires state to b e manipulated explicitly We allow

references to b e duplicated stored in data structures and so on while Clean do es

not Clean requires a new type system to b e explained to the programmer while our

system do es not On the other hand the separation b etween references and state

is sometimes tiresome For example while b oth systems can express the idea of a

mutable list Clean do es so more neatly b ecause there is less explicit dereferencing

The tradeo b etween implicit and explicit state in purelyfunctional languages is

far from clear

There are signicant similarities with Giord and Lucassens eect system which

uses types to record side eects p erformed by a program Giord Lucassen

However the eects system is designed to delimit the eect of side ef

fects which may o ccur as a result of evaluation Thus the semantic setting is still

one which relies on a predictable order of evaluation In another way though eect

systems are much more expressive than ours b ecause they provide a simple way to

describ e combinations of lo cal bits of state For example one might have

b f a

r eg ion

1

g c d

r eg ion

2

h e f

r eg ion [r eg ion

1 2

where h is dened using f and g The side eects of f and g cannot interfere with

each other since they mo dify dierent regions so they can pro ceed asynchronously

with resp ect to each other

Our work also has strong similarities with Odersky Rabin and Hudaks

v ar

Odersky Rabin Hudak which itself was inuenced by the Imp erative

Lambda Calculus ILC of Swarup Reddy Ireland ILC imp osed a rigid

stratication of applicative state reading and imp erative op erations The type of

48

runST makes this stratication unnecessary state op erations can b e encapsulated

and app ear purely functional This was also true of but there it was achieved

v ar

only through runtime checking which as a direct consequence precludes the style

of lazy state given here

Our use of parametricity to encapsulate state has echoes of work by OHearn

and Tennant OHearn Tennent In imp erative languages lo cal variables

ought to b e invisible to the the outside world Most mo dels of these languages how

ever do not reect this making mo delbased reasoning ab out program b ehaviour

less than satisfactory OHearn and Tennant discovered that by using Reynolds

style relational parametricity it was p ossible to construct mo dels for these languages

where lo cal variables were truly invisible from outside the scop e of the variable One

may view our work as extending this idea to the case where variables are rst class

values Then it is no longer sucient to restrict the application of parametricity

solely at the mo del level it b ecomes a source language concern instead

Halls recent work on listcompression in Haskell also has intriguing similarities

to ours Hall Hall introduces an extra type variable into the list type and

uses its unication or otherwise to determine the scop e of each list If a particular

application involving a list do es not have the extra type variable instantiated to

unoptimised then she replaces the list with a multiheaded version This is

rather similar to our use of universal quantication which in essence checks that

the statethreads state index is free of constraint

It also seems p ossible to use existential quantication to achieve encapsulation of

state In classical logic of course a universal quantier on the left of an arrow can

b e brought outwards converting to an existential in the pro cess In intuitionistic

logic this is not valid in general Nonetheless Thiemann has developed a system

which captures this Thiemann though the result is far more complex than

the metho d presented here

Acknowledgements

The idea of adding an extra type variable to distinguish state threads arose in

discussion with John Hughes and was presented briey at the Cop enhagen

workshop on State in Programming Languages though at that time we suggested

using an existential quantication in the type of runST

Philip Wadler made a number of insightful and helpful suggestions In addition

all these ideas have b eneted from discussions amongst the Functional Program

ming Group at Glasgow

References

Arvind RS Nikhil KK Pingali Oct Istructures data structures for

parallel computing TOPLAS

49

L Augustsson M Rittri D Synek Jan On generating unique names

Journal of Functional Programming

E Barendsen JEW Smetsers Dec Conventional and uniqueness typing

in graph rewrite systems in Pro c th Conference on the Foundations of

Software Technology and Theoretical Computer Science Springer Verlag

LNCS

PS Barth RS Nikhil Arvind Sept Mstructures extending a parallel

nonstrict functional language with state in Functional Programming

Languages and Computer Architecture Boston Hughes ed LNCS

Springer Verlag

RS Bird Using circular programs to eliminate multiple traversals of data

Acta Informatica

WN Chin March Automatic metho ds for program transformation PhD

thesis Imp erial College London

DK Giord JM Lucassen Aug Integrating functional and imp erative

programming in ACM Conference on Lisp and Functional Programming

MIT ACM

A Gill J Launchbury SL Peyton Jones June A short cut to deforestation

in Pro c Functional Programming Languages and Computer Architecture

Cop enhagen ACM

CV Hall June Using HindleyMilner type inference to optimise list rep

resentation in ACM Symp osium on Lisp and Functional Programming

Orlando ACM

P Hanco ck A type checker in The implementation of functional program

ming languages SL Peyton Jones ed Prentice Hall

John Hughes Apr Why functional programming matters The Computer

Journal

AJ Kfoury June Type reconstruction in nite rank fragments of second

order lambda calculus Information and Computation

AJ Kfoury JB Wells June A direct algorithm for type inference in the

rank fragment of the secondorder lambda calculus in ACM Symp osium

on Lisp and Functional Programming Orlando ACM

D King J Launchbury July Functional graph algorithms with depthrst

search in Glasgow Functional Programming Workshop Ayr

J Launchbury June Lazy imp erative programming in Pro c ACM Sigplan

Workshop on State in Programming Languages Cop enhagen available as

YALEUDCSRR Yale University pp

J Launchbury SL Peyton Jones June Lazy functional state threads in

SIGPLAN Symp osium on Programming Language Design and Implemen

tation PLDI Orlando ACM

50

S Marlow PL Wadler Deforestation for higherorder functions in Func

tional Programming Glasgow J Launchbury PM Sansom eds

Workshops in Computing Springer Verlag

NJ McCracken June The typechecking of programs with implicit type struc

ture in Semantics of data types Springer Verlag LNCS

R Milner M Tofte The denition of Standard ML MIT Press

JC Mitchell AR Meyer Secondorder logical relations in Logics of Pro

grams R Parikh ed Springer Verlag LNCS

E Moggi June Computational lambda calculus and monads in Logic in

Computer Science California IEEE

Rishiyur Nikhil March Id Reference Manual Lab for Computer Science

MIT

PW OHearn RD Tennent Jan Relational parametricity and lo cal vari

ables in th ACM Symp osium on Principles of Programming Languages

Charleston ACM

M Odersky D Rabin P Hudak Jan Call by name assignment and the

lambda calculus in th ACM Symp osium on Principles of Programming

Languages Charleston ACM

SL Peyton Jones The Implementation of Functional Programming Lan

guages Prentice Hall

SL Peyton Jones Apr Implementing lazy functional languages on sto ck

hardware the Spineless Tagless Gmachine Journal of Functional Pro

gramming

SL Peyton Jones J Launchbury Sept Unboxed values as rst class citi

zens in Functional Programming Languages and Computer Architecture

Boston Hughes ed LNCS Springer Verlag

SL Peyton Jones PL Wadler Jan Imp erative functional programming in

th ACM Symp osium on Principles of Programming Languages Charleston

ACM

CG Ponder PC McGeer A PC Ng June Are applicative languages inef

cient SIGPLAN Notices

JH Reppy June CML a higherorder concurrent language in ACM SIG

PLAN Conference on Programming Language Design and Implementation

PLDI ACM

DA Schmidt Apr Detecting global variables in denotational sp ecications

TOPLAS

V Swarup US Reddy E Ireland Sept Assignments for applicative lan

guages in Functional Programming Languages and Computer Architec

ture Boston Hughes ed LNCS Springer Verlag

51

P Thiemann Safe Sequencing of Assignments in Purely Functional Pro

gramming Languages Tech Rep ort WilhelmSchickardInstitut WSI

i T ubingenGermany

M Tofte Nov Type inference for p olymorphic references Information and

Computation

PL Wadler Deforestation transforming programs to eliminate trees The

oretical Computer Science

PL Wadler a Comprehending monads Mathematical Structures in Com

puter Science

PL Wadler Jan b The essence of functional programming in Pro c Princi

ples of Programming Languages ACM

PL Wadler June Comprehending monads in Pro c ACM Conference on

Lisp and Functional Programming Nice ACM

Notes

Actually the distributed system is rather richer as it includes some error handling facilities

also

Backquotes are Haskells notation for an inx op erator

The Ix i part of the type is just Haskells way of saying that the type a must b e an

index type that is there must b e a mapping of a value of type a to an oset in a linear

array Integers characters and tuples are automatically in the Ix class but array indexing is

not restricted to these Any type for which a mapping to Int is provided via an instance

declaration for the class Ix at that type will do

In Haskell the list of indexvalue pairs had type Assoc i v where Assoc is just a pairing

constructor but Haskell uses ordinary pairs in the list

The denition of indices shows up an interesting shortcoming in Haskells type signatures

which it shares with several other similar languages there is no way to give the correct type

signature to indices b ecause the type involves type variables lo cal to the enclosing deni

tion We cannot write indices iv b ecause that would mean that indices has type

8i v i v which is certainly not what we mean Instead a notation is required to allow the

type variables in the signature for newArr to scop e over the b o dy of newArr Here we simply

give the type of indices in a comment introduced by

Notice the type signatures in comments again

Indeed the formal semantics of an imp erative language often expresses the meaning of a

statement as a statetostate function

Because the let is recursive we also have to require r to b e strict

Indeed we have to admit that the implementation came rst