Imperative Functional Programming
Total Page:16
File Type:pdf, Size:1020Kb
Imp erative functional programming Simon L Peyton Jones Philip Wadler Dept of Computing Science University of Glasgow Email simonpjwadlerdcsglagsowacuk Octob er This paper appears in ACM Symposium on Principles Of Programming Languages POPL Charleston Jan pp This copy corrects a few minor typographical errors in the published version Abstract IO are constructed by gluing together smaller pro grams that do so Section Combined with higher We present a new mo del based on monads for p erform order functions and lazy evaluation this gives a ing inputoutput in a nonstrict purely functional lan highly expressive medium in which to express IO guage It is comp osable extensible ecient requires no p erforming computations Section quite the extensions to the type system and extends smo othly to reverse of the sentiment with which we b egan this incorp orate mixedlanguage working and inplace array section up dates We compare the monadic approach to IO with other standard approaches dialogues and continuations Section and eect systems and linear types Sec Introduction tion Inputoutput has always app eared to b e one of the less It is easily extensible The key to our implementation satisfactory features of purely functional languages t is to extend Haskell with a single form that allows one ting action into the functional paradigm feels like tting to call an any pro cedure written in the programming a square blo ck into a round hole Closely related dicul language C Kernighan Ritchie without ties are asso ciated with p erforming inplace up date op er losing referential transparency Section Using ations on arrays and calling arbitrary pro cedures written it programmers can readily extend the p ower of the in some other p ossibly sideeecting language IO system by writing Haskell functions which call op erating system pro cedures Some mostlyfunctional languages such as Lisp or SML deal successfully with inputoutput by using side eects It is ecient Our Haskell compiler has C as its We fo cus on purelyfunctional solutions which rule out target co de Given a Haskell program p erforming an side eects for two reasons Firstly the absence of side IO lo op the compiler can pro duce C co de which is eects p ermits unrestricted use of equational reasoning very similar to that which one would write by hand and program transformation Secondly we are interested Section in nonstrict languages in which the order of evaluation Its eciency is achieved by applying simple pro and hence the order of any side eects is delib erately gram transformations We use unboxed data types unsp ecied laziness and side eect are fundamentally in Peyton Jones Launchbury to exp ose rep imical resentation and orderofevaluation detail to co de There is no shortage of prop osals for inputoutput in lazy improving transformations rather than relying on ad functional languages some of which we survey later but hoc optimisations in the co de generator Section no one solution has b ecome accepted as the consensus It extends uniformly to provide interleaved IO and This pap er outlines a new approach based on monads reference types Section Moggi Wadler Wadler with a num b er of noteworthy features It extends uniformly to support incremental arrays with inplace update Section Our implementa It is composable Large programs which engage in tion is ecient enough that we can dene monolithic Haskell array op erations in terms of incremental ar mainIO IO rays Hudak have prop osed a similar metho d based mainIO putcIO on continuations Our metho d is more general than This is the p oint at which b eing is converted to doing his in the following sense monads can implement when executed the putcIO action will b e p erformed and continuations but not the converse write an exclamation mark to the standard output It is based only on the Hind leyMilner type system Some other prop osals require linear types or existen Comp osing IO op erations tial types ours do es not The functions dened ab ove allow one to dene a single We have implemented all that we describ e in the con action but how can actions b e combined For example text of a compiler for Haskell Hudak et al with how can we write a program to print two exclamation the exception of the extension to arrays and reference marks To do so we introduce two glue combinators types The entire IO system provided by our compiler doneIO IO is written in Haskell using the nonstandard extensions seqIO IO a IO b IO b we describ e b elow The languages standard Dialogue interface for IO is supp orted by providing a function to The comp ound action m seqIO n is p erformed by rst convert a Dialogue into our IO monad The system is p erforming m and then p erforming n returning whatever freely available by FTP n returns as the result of the comp ound action Back We do not claim any fundamental expressiveness or e quotes are Haskells syntax for an inx op erator The ciency which is not obtainable through existing systems action doneIO do es no IO and returns the unit value except where arrays are concerned Nevertheless we feel To illustrate here is an action putsIO which puts a that the entire system works particularly smo othly as a string to the standard output whole from the standp oint of b oth programmer and im putsIO Char IO plementor putsIO doneIO putsIO aas putcIO a seqIO Overview putsIO as We need a way to reconcile being with doing an expres We can now use putsIO to dene a program which prints sion in a functional language denotes a value while an hello twice IO command should perform an action We integrate mainIO hello seqIO hello these worlds by providing a type IO a denoting actions where that when performed may do some IO and then return hello putsIO hello a value of type a The following provide simple Unix avoured IO op erations This example illustrates the distinction b etween an action and its p erformance hello is an action which happ ens to getcIO IO Char b e p erformed twice The program is precisely equivalent putcIO Char IO to one in which putsIO hello is substituted for either Here getcIO is an action which when p erformed reads a or b oth of the o ccurrences of hello In short programs character from the standard input and returns that char remain referentially transparent acter and putcIO a is an action which when p erformed In general an action may also return a value Again writes the character a to the standard output Actions there are two combinators The rst is again trivial which have nothing interesting to return such as putcIO return the empty tuple whose type is also written unitIO a IO a Notice the distinction b etween an action and its p erfor If x is of type a then unitIO x denotes the action that mance Think of an action as a script which is p er when p erformed do es nothing save return x The second formed by executing it Actions themselves are rstclass combines two actions citizens How then are actions p erformed In our sys tem the value of the entire program is a single p erhaps bindIO IO a a IO b IO b large action called mainIO and the program is executed by p erforming this action For example the following is If m IO a and k a IO b then m bindIO k a legal Haskell program denotes the action that when p erformed b ehaves as fol lows rst p erform action m yielding a value x of type easier by dening new actionmanipulating combinators a then p erform action k x yielding a value y of type b For example the denition of putsIO given ab ove uses and then return value y To illustrate here is an action explicit recursion Here is an alternative way to write that echoes the standard input to the standard output putsIO which do es not do so In Haskell x e stands for a lambda abstraction the putsIO as seqsIO map putcIO as b o dy of the abstraction extends as far as p ossible The map applies putcIO to each character in the list as to echo IO pro duce a list of actions The combinator seqsIO takes echo getcIO bindIO a a list of actions and p erforms them in sequence that is if a eof then it encapsulates the recursion It is easy to dene seqsIO doneIO thus else putcIO a seqIO seqsIO IO a IO echo seqsIO doneIO seqsIO aas a seqIO seqsIO as The combinators bindIO and unitIO are generalisations of seqIO and doneIO Here are denitions for the latter or even using the standard listpro cessing function in terms of the former foldr thus doneIO unitIO seqsIO foldr seqIO doneIO m seqIO n m bindIO a n To take another example here is a function which writes The combinators have a useful algebra doneIO and a given number of spaces to the standard output seqIO form a monoid while bindIO and unitIO form a monad Moggi Wadler Wadler spaceIO Int IO spaceIO n seqsIO take n repeat putcIO Imp erative programming The functions take and repeat are standard list It will not have escap ed the readers notice that programs pro cessing functions with nothing to do with IO from written in the monadic style lo ok rather similar to imp er Haskells standard prelude The function repeat takes a ative programs For example the echo program in C value and returns an innite list each of whose elements might lo ok something like this is the given value The function take takes a prex of given length from a list echo loop a getchara These necessarily small examples could easily b e pro if a eof grammed with explicit recursion without signicant loss return of clarity or even a gain The p oint we are making is else putchara that it is easy for the programmer to dene new glue to goto loop