A popular meme in the world of PL’s is that “Haskell has monads”, with the implication that this is a distinctive feature of the language, separate from all others. While it is true that Haskell has popularized the use of monads as a program structuring device, the idea of a monad is not so much an issue of language design (apart from the ad hoc syntactic support provided by Haskell), but rather one of library design. After all, a monad is just one of a zillion signatures (type classes) with which to structure programs, and there is no particular reason why this one cannot be used in any language that supports even a modicum of modularity.
Examined from the point of view of ML, monads are but a particular of use of modules. The signature of monads is given by the definition
signature MONAD = sig type 'a monad val ret : 'a -> 'a monad val bnd : 'a monad -> ('a -> 'b monad) -> 'b monad end
There are many, many, many structures that satisfy this signature; I needn’t (and, in any case, can’t) rehearse them all here. One particularly simple example should suffice to give the general idea:
structure Option : MONAD = struct type 'a monad = 'a option fun ret x = SOME x fun bnd (SOME x) k = k x | bnd NONE k = NONE end
This is of course the option monad, which is sometimes used to model the data flow aspects of exceptions, perhaps with some elaboration of the NONE case to associate an exceptional value with a non-result. (The control flow aspects are not properly modeled this way, however. For that one needs, in addition, access to some sort of jump mechanism.)
Examples like this one proliferate. A monad is represented by a structure. Any structure that provides the facilities specified by the MONAD signature gives rise to the characteristic sequentialization mechanisms codified by it. Monad transformers are functors that transform one monad into another, with no fuss or bother, and no ad hoc mechanisms required. Standard modular programming techniques suffice to represent monads; moreover, the techniques involved are fully general, and are equally applicable to other signatures of interest (arrows, or quivers, or bows, or what have you). Moreover, it is shown in my paper with Chakravarty, Dreyer, and Keller how to integrate modules into the type inference mechanism of ML so that one can get automatic functor instantiation in those limited cases where it is self-evident what is intended. This has been implemented by Karl Crary in a prototype compiler for an extension of Standard ML, and it would be good to see this supported in more broadly available compilers for the language.
The bulk of the mania about monads is therefore accounted for by modules. I have no doubt, however, that you are wondering about the IO monad in Haskell. Isn’t that a fundamental feature of the language that cannot be replicated in ML? Hardly! It’s entirely a matter of designing the signatures of the standard basis library modules, and nothing more. The default basis library does not attempt to segregate effects into a monad, but it is perfectly straightforward to do this yourself, by providing your own layer over the standard basis, or to reorganize the standard basis to enforce the separation. For example, the signature of reference cells might look like this:
signature REF = sig type 'a ref val ref : 'a -> 'a ref IO.monad val ! : 'a ref -> 'a IO.monad val := : 'a ref -> 'a -> unit IO.monad end
Here we are presuming that we have a fixed declaration
... continue reading