Roman Cheplyaka

Monads in dynamic languages

Published on December 26, 2012; tags: Haskell

Are monads possible outside of Haskell?

Of course, it depends on what you call a monad.

The definition that is usually accepted in the Haskell context — that a monad is an instance of the particular type constructor class, Monad, with two methods satisfying three laws — is very much tied to Haskell’s language features — most importantly, types, type constructors and type constructor classes. Hence, this or similar notion of monad can only exist in a language rather close to Haskell.

If we drop the type constructor class requirement, then a monad is a triple consisting of a type constructor M and two polymorphic functions

return :: forall a . a -> M a
(>>=) :: forall a b . M a -> (a -> M b) -> M b

This is the definition originally used by Wadler in The essence of functional programming. By this definition, Monads can be implemented in other Hindley-Milner based languages, like Miranda and ML.

But what if we take this to extreme? What if we consider a dynamically typed functional language?

A dynamically typed language is nothing else than a static language with a single type, which we shall call here U. Thus, the signatures of >>= and return become

return :: U -> U
(>>=) :: U -> (U -> U) -> U

(We could as well say that >>= and return both have type U — remember, there’s only one type!) The functions themselves are still presumed to satisfy the usual laws.

This has been done many times before, most recent example being Douglas Crockford’s keynote at YUIConf. Douglas’s presentation has received a lot of criticism (and I’m not going to defend him), but some critics even have expressed the doubt that these are not «genuine» monads.

To settle this, let’s turn to the category theory — after all, that’s where monads come from. Haskell’s monads are category theory monads, where the type constructor is considered as an endofunctor on Hask, the category of Haskell types and functions.

What changes when we go from Haskell to a unityped language is that all the diversity of Haskell types collapses into the single type, U. That doesn’t stop it from forming a category, though. Because it has a single object, we don’t need a type constructor to represent the functorial action on objects — it’s always identity. We only need to know the functorial action on arrows (called fmap in Haskell) — but it is already determined by return and >>=.

To summarize, there doesn’t seem to be any reason to think that monads are somehow impossible in dynamic functional languages. And, although Haskellers usually consider monads as something closely tied to types, in a dynamic language all you need to define a monad is to define a pair of functions satisfying the monad laws.