You are on page 1of 11
J. Furevional Programming 3 (1): HCD, Jamey 1968 © 1666 Cambaedge University Pres 1 FUNCTIONAL PEARLS A Poor Man’s s Concurrency Monad Koen Claessen hatnert Univenity of Teckroloay ‘mail lomdeschelmusst Abstract Without aking any primitives to the langue, we define a conaurency mond tare fermer in Herlel. This allows us to ad a limited form of concmency to any erésting torah The atomic action of the new mona ae lifes! actions of the underlying mona. Some entra opemitions, sch as fork, to initiate new processes ae provid. We diets the implementation, and tse same enamples to istrate the usefulness of this ecnstruce 1 Introduction "The concept of a monad (Wadler, 1965) is nowadays heavily used! in modern func. tional programming languages. Monads are used to model seme form of computa tion, such as non-determinsm or a stateful calculation, Not cay does this solve ‘many of the traditional problems in fnctional programming, such as 1/O anel m- table state, but it also offers a general framework that abstracts over many Kinds of computation, Il is known how to use monads to model concurrency. ‘To do this, one usually constructs an imperative monad, with eperations that wsemble the Uni fozie (ones & Huck, 1993). For reasons of efficiency and control, Concurrent Haukell (Peyton Jones ef al 1996) eve provitles primitive operationss which are defined outside the Tanguage, ‘This paper presents a way tomodel concurrency, generalising over arbitrary mene ads, ‘The idea is to have afomic actions in some mons that can be died into a concurrent setting, We caplore this idea within the languages we will not add any primitives. 2 Monads “To eapress the properties of monads in Haskell, we will use the following type class definition. The bind operator of the monad is dencted by (4), anel the unit operator ly reeumn, Koen Clacven clans Monad m where (s) ma+(a+mg)+m xetum = ama Furthermore, throughout tic suga paper wo will use the so-called do-notation as syntac- 1 for monadlic capressions. The following ewample ilustrates a traditional ronadlic empression on the left, and the same, written in do-notation, on the right. epr, + A dor + er er, + he 5 expr ems + Me ly © expres vebumn expr ¢ retum expr As an emple, we present a monad with output, called the writer: monad This ‘monad has an entra operator called write. It tales a string as angument, which bhecomes output in a side effect of the mona‘. The bind operator (4) of the monad has to take care of ecmbining the output of tro computations. A monad having this eperator is an instance of the fellowing class. clare Monad m = Writer m where write = String + m( A typical implementation of such a menad is a pair ccntaining the result of the ‘computation, together with the output produond during that computation, type War = (oy String) imstance Monad W vhere (a, 1) «ke = Let (b, retum x = (x, “) bain (0, 1) instance Writer W shere write 1= (0s) Note how the hind operator concatenate the output of the two subactiens. Most menadls come equipped with a run fiction, This fiction executes a ccm putatien, taking the values inside one level downwards. The mond W has such a run function, we call it output, which returns the output of a computation in W. output Was string output (a, 1) = 1 Functional pearl 3 2L Monad Trantformert Sometimes a monad is parametrisel over ancther monad. This is mextly done to ade more functionality to an existing mona In this case we speak of a mona danformer (Liang et a, 1985). An anzmple is the ewception mone transformer; itadds a way to escape a monadic cemputaticn with an error masage. In general, operations that work en one specific manad can be lifted into the new, extended nomad. Again, we can cpress this ly using a type class clas KonadTrant 7 where Lift = Monad m = ma (rma A type constructor 7 forms a monad transformer ifthere is an operation Lift that transforms any action in a mona! 1m into an action in a monad 7 on. Tn this paper we will discuss a monad transformer callod C. It has the interesting property that any monaslic action that is Tifted into the new monad will be com sidered] an afomic action in a cencurrent setting. Also scme entra operations are provided for this monad, for enample fork, which deals with process initiation, 3 Conewrency How are we going to model concurrency? Sinoe we are not allowed to add primi- tives to the language, we are going to sirmlate concurrent processes by inlerdeaning them, Interleaving implements.concurrency by running the fist part of one process, suspending it, and then allowing another process to run, 3.1 Continuation ‘To suspend a process, we need to grab its future and stick it away for later use. Contirmations are an encellent. vay of cing this We can changp a function into continuation parwing wile by adding an esstra parameter, the continuation. Instead cf prevtucing the resilt dint, the functica will now apply the contintation to the rest, We can view the continuatien as the fiture of the computation, as it specifies what to do with the result ef the functicn. Given a computation type Acton, a function that uses a continuation with rest type a has the following type, type Ca = (a+ Action) —+ Action ‘The type Action contains the actual computation. Since, in our case, we want to parametrise this over an arbitrary monad, we want Action (and also 6) to be dependent on a monad m, type C ma = (a+ Action m) — Action mm 4 Koen Clacven is the concurrency monadl transformer we use in this paper. That means that ¢ misa monad, for every monad m. instance Monad m= Monad (Cm) vhere fre = de fOQukac) xetum 2 = Mex Sequencing of continuations is done by creating a new continuation for the left ‘computation that ccntains the right computation, The unit operator just passes its argument. to the continuation, Actions ‘The type Action m specifies the actual actions we can doin the new monael. What does this type look like? For reasons of simplicity, flemibility, and enpressivencss (Scholu, 1995), we implement it as a datatype that describes the different actions ‘we provide in the mons. First ofall, we need atoms, which are computationsin the monadm, Weare inside a continuation, so we want these atomic computations to return anew action, Alo, ‘we need a constructor for creating new processes. Lastly, we provide a constructor that dors not have a continuation; we will ws it to end a prooess, Wealso call this the empty proces. data Action m = Atom (in (Action mi) | Fork (Action m) (Action m) | Stop “To eapress the connection between an anpression of type © m a aml an expression of type Action m, we define a fiction action that transforms one into the other. It finishes the computation by giving it the Step contirmation, action action m Monad m= C ma— Action m m (a. Stop) ‘Tomake the contructons of the daiatype Action easily arassible, we can define functions that commspond to them, They will create an acticn in the monad C rm. "The fist fimetion isthe funetion atom, whi) tums an arbitrary computation in the monad m into an atomic action in € m, H¢runs the atomic computation and tmonadically retums a new action, ting the continnation.t atom atom i Monad m= ma—+€ ma De, Kean (do a +m rebum (© 4) This is actly the monadic map, hut beeasise Functor isnot asaperches of Nonad in Tikal pe earinct ose map. Funetional pearls 5 In addition, we have a function that uss the Stop constructor, called atop. I discards any continuation, thus encling a computation, stop =: Monad m=+C ma top = \c. Stop "To access Fork, we define two operations, ‘The first, called pax , combines to computations into one by forking them both, and passing the continuation to both parts. The second, fork, rscmbles the more traditional imperative fork. It forks its argument after tuning it into an action, and continues by paring () to the continuation, pear Monad m= € ma+€ ma+¢ma Par mm my = Me, Fork (mic) (ms ¢) fork Mona m= ma—+6m() fork m = de, Fork (action m) (¢ 0) ‘The type constructor ¢ is indeed a mone! transformer. Its lifting function is the function atom; every Tifted action Loccmes an atomic action in the ccncurrent settings inwtance MonadTrana C where ligt = atom We have nev defined ways to construct actions of type € 1m a, but we still can not do anything with them, How do we model concurrently running actions? How do ‘we interpret them? Semantic At any moment, the status of the computation is going to be modelled by a Tist of (concurrently running) actions. We will use a scheduling technique called rurale robin to interleave the processes. ‘The concept is easy: if there is an empty Tist of process, we are che, Otherwise, we take a prooss, run its first part, take the contimation, and put that at the back of the list. We keep doing this recursively until the list & empty. We implement this idea in the function round. round = Monad m= [ction mi] —+1 () round [] retum () round (a a1) = case aot ‘tom dn do a! + ay; round (a1 + (et) Fork ajay + round (at + [a1,a)) Stop round a 6 Koen Clacven An Atan monadically ercutes its argument, and puts the resulting prooss at the hack of the proces list. Fork creates two new proosses, and Stop discards its proces As for any monad, we nocd a run function for Cm as well, It just transforms its argument into an action, creates a singleton provess list, and applies the round-robin fimetion to it. mm = Nomad mC ma—+m() xan m = round [action mi] As we can see, the type a disappeams in the result type. This means that we lose the result of the original computation. This seems very eck, but often (and in the cases of the amples in this paper) we are only interested in the side effect of a computation. It is possible to generalise the type of zum, but that goes beyond the scope of this paper. 4 Examples We will present tro emamples of monads that can be lifted into the new concurrent world. Aol Concurrent Output Recall the writer monad easumple fiom Sect. 2. We can try lifting this mona into the concurrent, world, 'To do this, we want to say that every instance of a writer monad can be Tifted into a concurrent writer monad.t instance Writer m= Writer (C m) where write 1 = lit (write 1) ‘The function List here is the atom of the monad transformer C. Every write action, after lifting, becomes an atomic action. This means that no ccmputaticn vill produce output while another write is writing, Before we present an eaeanple, we first define an aumilary function Toop. ‘This fiction works in any writer monad. It takes one argument, a string, and writes it repeatedly to the cutput. Loop oop 1 Writer m= String +m (Q) do write 1; loop 1 ‘We use this function to define a computation in Cm a that creates two process that are constantly writing, One process writes the string “Eiah’, the other waites “cat”. + Actnally, we want to say this for all monad trareformers at once, Hatt Hashell does not ‘arent allow ts to pees tis, Functional pearl example :: Writer m>¢m() example = do write “start!” 3 fork (loop “Fish”) 5 loop “cat ‘The result of the expression output (un example ) looks ike the following string. “arart !fishcatfishcatfishcatfisheatfisheatfishca . Bacanse we defined write as an atomic action, the writing of one “fish” and one Scat” cannot interfere. If we want finer grained Lehaviour, we can split one write action into several write actions, e.g. the separate characters of a string, A simple ‘way of doing this is to change the lifting of write. instance Writer m = Writer (© m) where write [] return () write (¢:1) = do lift (write [e)) ; write 1 ‘The lifting is now cone character-by-character. ‘The result of the expression output (rm exemple) now looks like this. start !ciasthcfaituchafticeshtgcianthcfai tn 4-2 Merging of Infinite Lid A well known problem, called the merging of infinite ditty, is as fellows, Suppore ‘we have an infinite Tist of infinite Tiss, and want to collapse this list into cne big infinite list. The property we want to hol isthat every element in any of the original lists is reachable within a finite number of steps in the new Tist. This technique is fer cample usod to prexe that the set Q cf rationals has a countable number of elements. Using the writer monad with the new lifting, we can solve this problem for an infinite list of infinite strings. ‘The tea is that, for each string, We create a process that writes the string, If we fork this infinite number of prooeses, and run the resulting computation, the output will be the desired 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 is an cperator, dencted by (++), that combines two ccmputations of the same type into one, and that there is an identity element for this operation, called zezo.. In Haskell, we can say: laws Monad m= Monoidal mn where GH) = masmasma 8 Koen Clacven ‘The function coneat, with type Nonoidal m= [in a] +a, uses (4+) and zero to concatenate a (possibly infinite) list of such computations together. ‘The reason we are locking.at this is that Cm admits.a moncilal structure; the parallel compesition par represents the (4+), and the process wtop represents its identity element zero. inwtance Nonad m => Nonoidal. (C 1) where GH) = par zero = top ‘This means we can use concat to transfém an infinite Tist of procases into a precess that concurrently runs these computations. To merge an infinite list of infinite strings, we transform every string into a viriting process, fork them with concat:, and entract the output. merge = [String] + String merge = output © run o concat o map write Of course, this function also works for finite Tists, and can be alapted to act on ‘more general lists than strings 4-2 Concurrent State In Haskell, the so-called 10 menad provides mutable wate, Within the monad we can create, aco, andl update pieces of storage. The type ofa storage that contains an object of type a is Var a. The functions we use to control these Vacs, the nome proper morphistns of 10, have the following types. newer: 10 (Var 0) readVar i Ver a+ 10.0 weiteVar i Var aa 10 () In the lifted version ef this monad, the € 10. monad, we can have several concurrent processes sharing piooss of state. In a concurrent. world hewvever, we often want more structure on shared state, Concurrent Haskell (Peyton Jones ef al 1996), an ‘aensicn of Haskell with primitives fer creating concurrent, processes, mcognised this. It introduces a new form cf shared state: the MVar. Like a Var, an MVar can contain a value, but it may ako be empty. An War becomes empty’ after a process has done a read operation on it. Processes reading an empty Wax will block, until a new value is put into the Bar. Mars are a powerful mechanism for creating higher Fevel concurrent cata abstractions. They can for erample be used for synchronigtion and data sharing at the same time, It is possible to integrate MVaxs with our concurrency monad transformer, using, the mmtable state primitives we already have. First, we have to think of hove to represent an MVar. An War can be in two different states; it can either be fall (containing some value), or empty, Funetional pearls 9 type Vara = Var (Maybe a) data Maybe a = Just a | Nothing We use the datatype Maybe to indicate that there is Jumt a value in an Mar, oF Hothing at all. Tet us now define the operations that work on Mars. The function that creates: fan Max lifts the creation of a Var, and puts Nothing in i newer = C 10 (War 0) newer = Lift (do v near 5 writeVar 1 Nothing 5 retum 0) We can use the same trick when writing to an Wax! War a+ a-+6 10 0 Lift ( writeVar v (Just a) ) "The hardest function to define is read¥Var , since it has to deal with blocking, ‘To avoid interference when reading an Wax , we perform an atomic action that pulls the value out of the Var and putsNothing hack, We introduce an aumtilary function ‘takeVar,, working on the unlified 70 mend, that «locs this. takevar :: War a—+I0 @aybe a) takeVar v = do dy ¢ readVar 1 5 writeVar v Nothing j retumn dy, (One we have this fimction, the definition of a Hocking zeaaévar is not hard anymore, We represent blocking by repeatedly trying to real the variable, We realise that this busy-wait implementation is very inefficient, and we indeed have used other methods as well (such as the one used in lones, M. ef al., 1987), but ‘we present the easiest implementation here, reader: Wera+C 10a readMar v = do dy, + lift (takeVar 0) 4} CANe dy, OF Vothing —} readMVar v Jurt a + retuma Note that readMVar itself is not an atemic action, so other processes can also read the MVar just afler takeVar- Fortunately, at that point, the Mar is already blocked ly the functien takeVar. It is impessible for eadkVar to he atomic, since other processes deserve a chance when itis blocking on an Vax. 8 We are a bit sloppy heres the real semantics of MVars is slightly different (Peyton Janes: cet ale, 16). ote 10 Koen Coe For some earumples of the use of Maas, we refer the reader to the paper about Concurrent Haskell (Peyton Jones ct al 1996), where Mans are introcuced. 5 Discussion "The work presented in this paper isan anvellent ennmple of the fleiblity of mond and mond transformens. The pewer ef dealing with cliflerent types of computations in this way is very general, and should definitely be mone widely use and supported by programming languages. We eally had to push the Haskell type classmechanism to its limitsin order to make this work. slightly cartendad class mechanism would have been helpfil (Reytem Jones et aly 1997} "To show that this idea & more than just a toy, we have wed this same setting to add concurrency to the graphical system TkGofer (Vullinghs et a 1996). The system increased in eupeessive power, and its implementation in simplicity. It tums cut to be a very useful eatension to ThCoter. We have also aaperimented with lifting other well-laiowm monads into this oon current setting. Lifted lists, for errmmple, can be used to aupress the infinite merging problem more concisely, However, a problem with the type system fonond 1s to fool it in cxder to make this work. Faception ane! environment monadls (Wadler, 1995) do have the expected behaviour, though we are not able to lift all of the nom-proper ‘morphisms of these menad. This is because some of thern need a computation as an argument, 89 that lifting becomes noretxivial However, there area few dravrbacks. We have not implemented real concurrency. ‘We simply alle interleaving of afomac actions, whose atemicity plays a vital ole in the system, Ione atomic action itself does not terminate, the concurrent ccm- pultaticn of which it is a part of docs not terminate either. We cannot change this, because we cannot step outside the language to interrupt the evaluation of an eapresion, “The source oxele of the functions and classes mentioned in this paper is publi cally available at http,/www.ct.chalmert.te/+ koen/Code/(pearl.ht. It also contains another, more efficient at slightly bigger implementation of Hams Adaowledgements | would fil to thank Richard Bird, Byron Cook, Andrew Moran, ‘Thomas Nordin, Andrei Sabelfld, Mark Shields, ‘Ton Vullinghs, and Arjan van Yaendoom for their ‘useful comments on earlier drafts of this paper. Most of the work for this paper ‘vas done while visiting the Oregon Gracluate Institute, and an earlier version was used as part of my Master's thesis at the University of Utecht, under supervision of Erik Meijer. References Jones, Mu & Hudak P. (1953). Implicit andl Ealict Parallel Programming in Haste, ‘Vale University. "Tech, Rep. VALEU /DCS/RRCSSI, Functional pearl n Jones, Met al. (1297). The Hues System, Nottingham University and Yale University. “Unk htp:// wins, [Liang Sh. Tac, P.& Jonesy M, (1905), Monel Tarsformensannd Mochiba Interpeeters Conference Record of 22nd POPE ‘9. ACM. Peston Jones, S, Gorden, A. & Finne, 8 (1986). Concurent Haskell, Proveadings of the ‘Dard POPL "6, ACM. Peston Jones, Sw Jonesy My & Meier, E. (967). ‘Type Clases: An Fimpration of the ‘Design Stree Pracetings of the Haskell Workshop of the ICPF ‘9%, ACM. Sete, F. (1985). A Concmrency Monad Based on Constructer Primitives, Universit atin Vadlingley ‘Ts, Seite, Wo, & Schwinn, "T. 1995 (Jame). An Intredction to. The Gofer. Ted. wept, 960% University cf Ulm. Usk: tp:// wwu.informatikaun- tld pm ftp tgs hte Wadler, Ph, (1995). Menads for Functional Programming.

You might also like