Unknown Parallel


The Dead Simple, No Chit Chat, Zero-Analogy Haskell Monad Tutorial

This "tutorial" is intended to be readable by most anyone, assuming basic understanding of functions and values. I also assume a basic understanding of curried functions; if such is not the case, please glance over Haskell Wiki > Currying. Wikipedia > Currying may also help. Don't get let the subheadings scare you. I will attempt to briefly explain all requisite vocabulary, and to illuminate common vocabulary surrounding Monads as well. I will make no attempt to argue that Monads are a Good Thing, nor will I try to prove anything that I say here. I won't even mention any specific instances of Monads! I will merely attempt to describe, as simply as possible, what Monads are. I use the notation foo :: bar to signify either that "value foo has type bar", or "function foo has type bar", or "type foo has kind bar". We'll talk about kinds soon. For further information generally regarding this tutorial, see the final section of this page. With all that out of the way, let's get down to business.

Monad is a typeclass

A typeclass is a class of types that behave similarly.
If a type pertains to a given class of types, we say that type is an instance of that typeclass.

See Learn You a Haskell > Typeclasses 101 for a deeper introduction to typeclasses.

Monad is a typeclass, therefore certain types are Monads.
We therefore say these types are instances of the Monad typeclass. Saying "type Foo is a Monad" is shorthand for "type Foo is an instance of the Monad typeclass". Using the term Monads as the subject of a sentence is shorthand for "instances of the Monad typeclass". Values are not Monads, nor are functions. More on this later.

Monads are higher-kinded types

All Monads necessarily have the kind * -> *.
All types have a kind. Simple types like Bool, Char, and Int have the simplest kind: *. I call types with this simplest kind complete types. Types with the kind * -> * require another type in order to be complete. For example, Maybe has the kind * -> *. The type Maybe Int has kind *, because we gave the higher-kinded Maybe :: * -> * type another type (in this case, Int :: *) to make it complete. Types can have more complicated kinds than * and * -> *, but for discussing Monads, it is sufficient to only know about these two kinds.

See The Haskell Wikibook > Kinds for a deeper introduction to kinds.

Monads are Functors

All Monads are necessarily Functors.
Functor is a typeclass.
A Functor m defines fmap :: (a -> b) -> m a -> m b. In other words, if a type m :: * -> * is a functor, then there is some way to "get inside" that type and do stuff, and that "way" is fmap. Another way to think about it: if a type m :: * -> * is a functor, then there is some way to lift a function f :: a -> b so that it works on the same values, except "inside" a Functor: f' :: m a -> m b. In the world of Monads, fmap is sometimes called liftM.

Monads are Pointed

All Monads are necessarily Pointed.
Pointed is a typeclass.
Pointed types define pure :: a -> m a. In other words, if a type m :: * -> * is Pointed, then there is some way to take any value v :: a, and stuff it "inside" that type m in some default way, producing a value of type m a. In the world of Monads, values of type m a are often called Monadic values, or are said to be inside a monad. In the world of Monads, pure is often called return. This has absolutely nothing to do with "return" from other languages you may have programmed in. Never, ever, try to equate Haskell's return :: a -> m a with "return" from any other language.

Monads are Applicative Functors

All Monads are necessarily Applicative Functors.
Applicative is a typeclass, whose types are both Pointed and Functors.
In addition, an Applicative Functor m defines <*> :: m (a -> b) -> m a -> m b. In other words, if a type m :: * -> * is an Applicative Functor, then there is some way to apply a function to a value, when both are "inside" the Applicative Functor. Another way to think about it: if a type m :: * -> * is an Applicative Functor, then there is a way to take a function inside that Applicative Functor f :: m (a -> b) and turn it into a similar function that consumes and produces values inside the same Applicative Functor f' :: m a -> m b. In the world of Monads, <*> is sometimes called ap

Monadic values can be flattened and shoved

Recall that a Monadic value has the type m a where m :: * -> * is a Monad and a :: * is a complete type. If a value has the type m (m a), where m :: * -> * is a Monad, then it is a nested Monadic value, because it is a Monadic value inside a Monad. If a function f has the type a -> m b where m :: * -> * is a Monad, then it is a Monadic function, because its output is a Monadic value. (Note, a -> b -> m c is not a Monadic function, rather, its output is a Monadic function b -> m c) If a type m :: * -> * is a Monad, then beyond being a Applicative Functor (and therefore also Pointed), it defines join :: m (m a) -> m a, which is a way to "flatten" a nested Monadic value. It also defines bind :: m a -> (a -> m b) -> m b, which is a way to "shove" a Monadic value into a Monadic function.

These two functions, bind and join, can be defined in terms of each other. Sadly, Haskell (as of 2011) requires that you declare an instance of Monad by defining bind. In theory, a Monad is minimally defined with either {fmap, return, and join} or {return and >>= (bind)}.

    bind v f = join (fmap f v)
    join v   = bind v id

    id x = x

Monads get lots of functions for free

The Control.Monad library defines a few dozen functions that deal with Monadic functions and Monadic values. The interested learner should familiarize himself with these functions, and how they are defined. Among these, I wish to draw special attention to >=> :: (a -> m b) -> (b -> m c) -> a -> m c, which is the Monadic function composition operator. The concept of (a -> m b) -> (b -> m c) -> a -> m c is Monadic function composition, because it is the process of composing two Monadic functions.

See Wikipedia > Function composition for a general explanation of what function composition means.

Monads obey certain laws: Monadic function composition is associative

Just because you can define bind or join does not guarantee that you have a Monad. Monadic function composition must be associative, with return being the identity of Monadic function composition.

    f >=> return    ≡ f
    return >=> f    ≡ f
    f >=> (g >=> h) ≡ (f >=> g) >=> h

These laws can instead be written in terms of bind, which may be more useful if you are writing an instance of Monad in terms of bind, but conceptually they make a lot more sense in terms of the Monadic function composition operator.

See Haskell Wiki > Monad laws for a deeper introduction to the Monad laws.

Recap

Monads are simply Applicative Functors, which also define some way of flattening nested monadic values, and shoving monadic values into monadic functions. Applicative Functors are simply Functors, which are also Pointed, and which also define some way of applying a function to a value, when both are "inside" an Applicative Functor. Pointed types are simply types that define some way of stuffing a value "inside" of the Pointed type in some default way. Functors are simply types that define some way of "lifting" a function so that it operates "inside of" the Functor.

In addition, Monadic functions are composable, and Monadic function composition is associative. A Monad can be minimally defined in terms of either {fmap, return, and join} or {return and >>= (bind)}. From this definition, many handy functions can be derived automatically.

For a more in-depth look at the Functor, Pointed, Applicative, and Monad typeclasses (and more!) check out the typeclassopedia. The format and content of this tutorial is heavily inspired by the typeclassopedia: special thanks to Brent Yorgey for that incredible document.


Regarding this tutorial

Monad tutorials are practically required to give analogies. While I do give examples, I refuse to make any analogies. If you are absolutely dying to hear an anlogy from me, see my superpowers in a bubble analogy. In fairness, some of the phrases that I put inside of quotation marks could be considered weak analogies. So sue me. Also, technically, I lied when I said I wouldn't mention instances of Monad, since I used Maybe Int when describing kinds. But I never described how Maybe is a Monad, which is what I meant.

It's common to abbreviate books or references in the Haskell community (YAHT, LYAH, RWH, etc). TDSNCCZAHMT is a little much. So if you ever feel the need to refer to this tutorial, I think "Zero-Analogy Monad Tutorial" (ZAMT) will suffice.

A word about my terminology: the Control.Monad docs use the term "a function in the Kleisli category", which is close to, but not entirely analagous to what I called a "Monadic function". Also, what I call "Monadic function composition", the Control.Monad docs apparently call "Kleisli composition of monads". I believe the way I use the terms "Monadic function" and "Monadic value" is in line with the way the Haskell community typically uses them; if not, let me know. Also, there are issues with using the term "Pointed", but I use it anyways, because that's what typeclassopedia uses. So if you are one of those people who is big on category theory, take that part with a grain of salt.

P.S. if you know of any really great explanations of currying that I can link to, please let me know.