You are on page 1of 11
J. Fimotional Programming 1 (1): 4000, Jaruary 1968 © 1950 Cambridge University Press FUNCTIONAL PEARLS A Poor Man’s fonad Concurrency J Koen Claessen Chalmers University of Tecknalagy emails kone. chemist Abstract Without adking any primitives to the binguage, we define a concomteney mend tree former in. Haske, This allows ws to ack a limited ferm of emcurency to any existing zmonack The atomic actions of the new manad are lifted acticns of the unesiving monad. Some entra operations, such as fark, to initia‘e new process, are provided. We discuss the implementation, an use some namples to illustrate the usefulness of this construc tion. 1 Introduction ‘The concept of a monad (Wexler, 1995) is nowarlayss heavily used in modern fimo= tional programming languages. Mens are used to model some form of compute tion, such as nonsleterminism cr a stateful calculation, Not only does this solve many of the traditional problems in functional programming, such as I/O and mu table state, but it also offen a general framework that abstracts over many Kinds of computation, Tt is known how to use monadls to model concurrency. ‘To do this, one usually constructs an imperative monad, with operations that resemble the Unim fork Glones {& Hudak, 1993). For reasons of efficiency and control, Concurrent Ha veel (Peyton Jones et al, 1996) even provides primitive operations, which ane defined outside the language. ‘This paper presents a way to model concurrency, generalising over arbitrary mon- als. The idea is to have atomic actions in some mone! that can be lif! into a concurrent setting. We earplore this idea within the language; we will not add any primitives. 2 Monads “To earpmess the properties of monaels in Haskell, we will use the follwing type class definition, ‘The bind operator of the mona is denoted by (4), and the unit operatce by return. Koen Clae wen claws Monad m where @) mas(atmiams retum ama Furthermore, throughout this paper we will use the so-called dosnotation as syntac= tic sugar for monadic eapressions. The following erumple illustrates a traditional monadic enpression on the left, and the same, written in do-notation, cn the ri expr: + dn dor + expr: expr +L. | ere expr + Jy. sy © eps return expr retum expr y As an errmple, we present a monad with output, called the writer monad. This mona! has an eta cperator called write. It takes a string as argument, whi ‘ecomes output in a side effect of the monad. The bind cperator (4) of the monad hhas to take care of combining the output of two computations \ monad having this operator is an instance of the following class. clams Monad 1m = tiriter m where write = String + m() A typical implementation of such a monad is a pair containing the result of the computation, together with the output produced during that computation, type Wa = (a, String) inrtance Monad W there (a 1) xk = Iet (0, ¥) = hain 6, bY) retum 2 = (2, “) inrtance Writer W where write 1 = (1) Note how the bind operator concatenates the output of the two subactions. Mest monads come equipped with a run function, ‘This function erecutes a come puiaticn, taking the values inside one level dowrwwards. The menad W has such a run finction, we call it output, which returns the output of a computation in W. output Wa String output (a, 1) = 1 Functional pearls 3 2.1 Monad Tranyformert Sometimes, a menad is parametrisad over ancther monad. This is mostly done to ‘xd more functionality to an existing monad. Tn this case we speak of a monad trunsformer (Liang et al, 1905). An ewsanple isthe enception monad transformer, it adls a way tocscape a monatlic computation with an error mesagp. In general, operations that work on one specific monaxl can be lifld into the new, artendes mona. Again, we can empress this by using a type class. lane MonadTrans 7 where Lift = Monad m > ma—+(rm)a type constructor 7 forms monad transformer if there is an operation Lift that ‘transforms any action in a monad m into an action ina monad 7m. ‘In this paper we will discuss a monad transformer called G. It has the interesting property that any monavlic action that is lifted into the new monael will be con Sklered an atomic action in a concurrent. setting, so some entra operations are provided for this mona, for enrumple fork, which deals with process initiation, 3 Concurrency How are we geing to model concurrency? Since we are not allowed to add primi- tives to the language, we are going tosimulate concurrent processes by interleaving ‘hem. Interleaving implementsconcurrency by running the first part of one process, suspending it, and then allowing another precess to run. 3.1 Continuations “To suspend a process, we nec to grab its future and stick it away for later use, Continuation are an eavdllent: way of doing this. We can change a function into continuation paving wyle by adding an entra parameter, the contimation, Instead of proshicing the result directly, the function will now apply the continuation to ‘the result. We can view the contimation as the future of the computation, as it specifies what to do with the result of the fianction, Given a computation type Action a fimction that ses a continuation with result type a:has the fellowing type. type Ca = (a Action) - Action ‘The type Action contains the actual computation, Since, in our case, we want to parampitise this over an arbitrary monad, we want Action (and also €) to be dependent on a monad m. type C ma = (a—+ Action m)— Action m 4 Koen Clae wen jis the concurrency monad transformer we use in this paper. ‘That means that Cm isa menad, fer every monadl rm. imtance Monad m= Monad (C m) where Sak de. f Qa. k ae) retum 2 = Meer Sequencing of continuations is done ly creating a new contimiation for the left ‘computation that contains the right computation. The unit operator just passes its argument to the continuation. 3.2 Actions ‘The type Action m specifies the actual actions we can do in the new monad. What does this type Jook like? For reasons of simplicity, flenibility, and expressiveness, (Schola, 1995), we implement it as a datatype that describes the different actions wwe provide in the monad. First ofall, weneed atoms, which are computations in the monadm, Weare inside continuation, so we want these atomic computations to retum a new action. Also, ‘we nee! a constructor for creating new processes. Lastly, we previde a constructor ‘that does not have a continuation; we will use it to end a proces We also call this the empty proces data Action m = Atom (m (Action m)) | Fork (Action m) (Action m) | stop ‘To eupress the connection between an enpresion of type Cm a and an apression, of type Action m, we define a function action that transfonns cne into the other. Tt finishes the computation by giving it the Stop contirmation, action Monad m => C ma} Action m action m = m (Mn Stop) "To make the constructors of the datatype Action easily acossible, we can define functions that correspond to them, ‘They will create an action in the monad m. ‘The first fection is the function atom, which tums an arbitrary computation in ‘the monad m into an atomic action in € m. It rms the atomic ccmputation and, monevlically retums a new action, using the continuation.t atom: Monad m= ma+¢ ma atom m = Ae, Atom (do a Hm ; return (© a)) "This is actually the momdic map, bat because Functor isnot a superclass of Nona in ‘Haskell we cannot use map. Functional peards 5 In addition, we have a function that uses the Stop constructor, called atop. It discards any contimmation, thn ending a computation, atop = Monad m => C ma rtop = Ae, Stop "To anes Fork. we define two operations, The first, called parr, combines two computations into one by forking them both, and pessing the continration to both parts. The senond, fork, resembles the more traditional imperative fork. Tt forks, its argument after tuming it into an action, and continues by passing () to the continuation. par Yonad m=>C ma+C ma+cma ar mm = Ac, Fork (mm; ¢) (19 ¢) fork Yonad m=>C mac m () fork m =e, Fork (action m) (c ()} ‘The type consinictor ¢ is indeed a monad transformer, Tts lifting fimetion is the function atom; every lifted action becomes an atomic action in the concurrent setting. ingtance NonadTranu C where lift = atom We have none defined ways to construct actions of type € ma, but we still can not cb anything with them, How do we model concurrently: miming actions? How do swe interpret them? 3 Semantics At any moment, the status of the computation is going to he modelled by a Tist of (concurrently runing) actions, We will use a scheculing, technique called round- robin to interleave the process. The concept is easy if there is an empty list of processes, we are done, Otherwise, we take a process, run its first part, take the continuation, and put that at the back of the list. We keep doing this recursively until the list is empt We implement t idea in the function round. round =: Yonad m= [Action ni] + m (Q) round [] return () round (a:ai) = cate aot KtoM dm dO of Gy 5 YoUnd (at + [or]) Fork ajay + round (a1 ++ [aia Stop + round a 6 Koen Clae wen An Ata monadically eaecutes its argument, and puts the resulting proms at the back of the process list. Fork creates two new prooases, and Stop d process, As for any monad, we need! a run function for € m as well. I just transforms its argument into an action, creates a singleton process list, and applies the reundsnobin function to it. cards its mm: Monad m= ma—+mQ run m = round [action m] As we can see, the type a disappears in the result type. This means that we lese the result of the original computation, This seems very odd, but often (and in the cases of the enamples in this paper) we are only interested in the wide effect of a computation. It is possible to generalise the type of nm, butt that goes beyond the srqpe of this paper. 4 Examples We will present two eramples of monads that can be lifted into the new concurrent world. 4.1 Concurrent Output Recall the writer menad eaample from Sect. 2. We can try lifting this monad into the concurrent world. ‘To do this, we want to say that every instance of a writer monael can be lifted into a concurrent writer menad.# inrtance Writer m= Writer (Cm) where write 1 = lift (write 1) ‘The fiction 14¢t hew is the atom of the monad transformer C. Every write action, after lifting, becomes an atomic action, This means that no computation will produce output while another write is writing, Before we present an atample, we first define an aunilary function Loop. This function works in any writer monad. It takes one argument, a string, and writes it repeatedly to the output. Joop :: Writer m= String - m() loop 1 = do write 1; loop 1 We use this functicn to define a computation in Cm a that creates two processes that are constantly writing. One process writes the string “finh', the other writes cat * Actually yee want to say this forall mana transformers at once, but Haskell does not ‘eurrenily allow ts to empress this. Functional peards example ©: Writer m>¢m(Q) example = do write “start! fork (loop ‘fish’ Joop “cat! ‘The result of the eupression output (run example ) looks like the following str start! shcatfishcatfishcatfishcatfishcatfishea ...” Because we defined write as an atomic action, the writing of one “#4” and one “cat cannot interfere. If we want finer grained behaviour, we can split ene write action into several write actions, eg. the separate characters of a string, A simple ‘way of doing this isto change the lifting of write. instance Writer m= Writer (Cm) where wite[] = retum () write (c:1) = do Litt (write [¢)); write 1 ‘The lifting is now done character-by-character. The result of the eapression output (an example) now looks like this. “startlfciasthcfaituchaf ticrahtéciarthefaitacha 4.2 Merging of Infinite Liv A well known problem, called the merging of infinite ltd, % as follonss. Suppose ‘we have an infinite Hist of infinite lists, and want to collapse this Hist into one big infinite list. The property we want to hold is that every element in any of the original lists is teachable within a finite number of steps in the nevw lst. This technique is for eaample used to prove that the set Q of rationals has a countable ramber of laments. ‘Using the writer monad with the new Hifing, we can solve this problem for an infinite Hist of infinite strings. The idea is that, for each string, we create a proorss that writes the string. If we fork this infinite number of process, and run the resulting computation, the output will ke the desived infinite string. We will take a step back in order to present a piece of useful theory. There are ‘monads that have a so-called monoidal structure on them. That means that there isan operator, denoted ty (4+), that combines two computations of the same type into one, and that there is an identity element for this operation, called zero. In Haskell, we can say lawn Monad m => Monoidal m where G+) = masmasma 8 Koen Clae wen ‘The function concat, with type Monoidal m= [in a] +a, ues (44) and zero to concatenate a (possibly infinite) list of such computations together. “The reason we are locking at this is that ¢ m admits a monoidal structure; the parallel compesition par represents the (4+), and the pros atop represents its identity element zero. inwtance Monad m = Nonoidal (C m) where G+) = par zero = stop ‘This means we can use concat to transform an infinite list of proves into a process that concurrently runs these computaticns. ‘To merge an infinite list of infinite strings, we transform every string into a writing process, fork them concat, and entract the output. Of compe, this function also works for finite lists, and can be adapted to act on. ‘more general lists than strings. 4.3 Concurrent State Tn Haste, the socalled 10 monad provides mufable wate. Within the monad we can create, anness, and update pieces of storage. The type ofa storage that contains an object of type a is Var a. The functions we use tocontra these Vars, the non proper merphisms of TO, have the following types. newar = 10 (Var a) veadVer = Var a+ 10.0 witeVar = Var a $0410 () Jn the lifted version of this monad, the € 10 monad, we can have several concurrent processes sharing pieces of state. In a concurrent work! however, we often want ‘more structure on shared state. Concurrent Haskell (Peyten Jones ct a, 1996), an antension of Haskell with primitives for creating concurrent. processes, recognised this. It introduces a new form of shared state: the MVar. ike a Var, an War can contain a value, but it may also be empty. An Var becomes empty after a process has elene a read operation on it. Processes reading an empty Mar will block, until a new value is put into the War. MVaxs are a powerful mechanism for creating higher level concurrent data abstractions. ‘They can for emmple be wee for synchronigntion and data sharing at the same time. Te is possible to integrate MVars with our ccncumency monad transformer, using, the mutable state primitives we already have. First, we have to ¢ represent an War. An MVar can be in two different states; it can (containing some value), or empty. Functional pears 9 ‘type Hvar a data Maybe a = Var (Maybe «) = Just a | Nothing We use the datatype Maybe to indicate that there is Just’ a value in an Mar, or Nothing at all. et us now define the operations that work on MVaxs. The function that creates an Vax lifts the creation of a Vax, and puts Nothing init. newiVar = C10 (iar a) newiVar = lift (do v —newvar 5 writellar u Nothing ; retum v) We can use the same trick when writing to an MVar writelVar War a+ a+ 100 writelWar va = lift (writevar v (Just a) ) ‘The hardest function to define is readlVar, since it has to deal with blocking, ‘To avoid interference when reading an War, we perform an atomic acticn that pulls the value out of the Var and putsNothing back. We introduce an aumilary function ‘takeVar, working on the unliftel 10 mona, that does this. takeVar =: Mar a— 10 (Maybe a) ‘takeVar v = do dm + readVar v writeVar v Nothing 5 Tetum dy, Ome we have this function, the definition of a blocking readifvar is not hare anymore. We represent Hocking by repeatedly trying to read the variable, We realise that this buy-wait implementation is very inefficient, and we indeed have used other methods as well (such as the one wed in (Jones, M. et al 1997)), but swe present the easiest implementation here. reader: Mar a+¢ 10 a readMVar 1 = do dn + Litt (takeVar 0) 5} CaN diy, Of Wothing + readiWar v Justa reemm a Note that readiVar itself is not an atomic action, so other processes can also read the War just after takeVar. Fortunately, at that point, the Mar is already blocked bby the fimetion takeVar. It is impossible for readMVar to Le atomic, sinoe other processes deserve a chance when it is Hocking on an Kar « 8 We are a bit sloppy here; the real semantics of MVars is slightly dlfferent (Peyton Jones etal 18). 10 Koen Clae wen For some eamimples of the use of Mars, we refer the reader to the paper about Concurrent Haskell (Peyton Jones et al, 1996), where Hans are introduced. 5 Discussion “The work presented in this paper isan emellent casmple of the flaniblity of monacls, and monad transformers. The power of dealing with different types of computations, in this way is very general, and should definitely be mere widely used antl supperted bby programming languages. We really had topush the Haskell type class mechanism, {0 its limitsin order to make this work. A slightly emtended classs mechanism woul have been helpful (Peyton Jones et al 1997). ‘To show that this idea is more than just a toy, we have used this same setting, to ack] concurency to the graphical system TkGiofer (Vullinghs ct aly 1996). The system increased in empressive power, and its implementation in simplicity. Ie tums ott to be a very usefil emiension to TkGofer. We have also arperimented with lifting other wdleknewn monadls into this cone camrent setting, Lifled lists, for enmple,can be used to express the infinite merging, problem more concisely, Hewever, a problem with the type system forced us to fool it in order to make this work. Earxption and environment menads (Wadley 1995) do have the ampected! behaviour, though we are not able to lift all of the noneproper ‘morphisms of these monals. This is becanse some of them need a computation as an agument, so that lifting becomes non-trivial. ‘However, there are a few draiwhacks, We have not implemented read concurrency’ We simply allow interleaving of atomic actions, whose atomicity plays a vital role in the system. Tone atomic action itself does not terminate, the concurrent com putation of which it is a part of does not terminate either. We cannot change this, because we cannot step outside the language to interrupt the evaluation of an aupresion. “The source code of the functions aml classes mentioned in this paper is publi cally available at http://www.ct.chalmerte/s koen/Code/peartha. It also contains amcther, more efficent. but slightly bigger implementation of Hans. Adaowledgements Twould like to thank Richard Bird, Byron Cook, Andrew Moran, ‘Thomas Nordin, Andrei Sabelfeld, Mark Shields, ‘Ton Vullinghs, and Arjan van Yaendoomn for their usefull comments on earlier drafts of this paper. Most of the work for this paper vwas done while visiting the Oregon Graduate Institute, and an earlier version was used as part of my Master's thesis at the University of Utrecht, under supervision of Erik Meijer. References, JJenesy Mo, Tak P, (1988). Implicit and Fawaict Parallel Programming in Maskell, Yale University. ‘Tech. Rep. YALEU/DCS/RR81. Functional pearls u Jones M, et al. (1997). ‘The Figs System. Nottingham University and Yale Universit ‘Unk http:// wwnwhaskillong, Liang, Sh.,Hiudak, Pe Jones, M, (1985), Monad ‘Transformers and Modular Interpreters. Conference Reconi of 22nd POPE. 95. ACM. Peston Janes, S., Gordon, x & Finne, 8, (1956). Concurrent Haskell, Proceedings ofthe ‘2ind POPL, ‘96, ACM. Peyten Jones, S. Jones, M., & Meijer, E. (1997). Type Classes: An Exoration of the ‘Desien Stnce. Procredings ofthe Haskell Workshop of the IOPF 9, ACM. Scholz, F. (1995). A Conamrency Manad Based an Constructor Primitives. Universitit Berlin, Viilinglss Ts, Sdaite, Wy & Selvinn, T. 1986 (hime). An Intreduetion to. The Gofer.” Tech. rept, 960% University of Ulm. Unt hite//unwinformatikaune ulmat/ pm ftp/ tkeof shir. Wadler, Ph, (1905). Monads for Functional Programming, Advanced Functional Programe sing. Lecture Notes in Computer Science. Springer Verlag,

You might also like