This is an introduction to monads. There are many of these. My goal today is to show how a simple class written in Java could be translated into equivalent functionality in Haskell using some monads, without getting into any of the theory stuff.

Hopefully some people coming from a non-Haskell background will get something out of this, though the Haskell syntax is likely to be very WTF-inducing for those who haven’t seen it before.

I should begin with a few things that this guide is not about:

Categories. The etymology of the word “monad” is a red herring. Trust me. Knowing a lot about category theory will make you a better programmer in the same way that playing a lot of checkers will make you better at chess: there’s probably some benefit, but it’s not a good way to get up and running with the basics. We will be treating monads as a design pattern instead of monoids in the category of endofunctors . The latter perspective is interesting to the people who like to design languages; the former perspective is interesting to people who like to write code.

benefit, but it’s not a good way to get up and running with the basics. The full generality of monads in programming. Lots of things are monads, and I will be ignoring most of them. I’m going to focus on how monads can be used to translate a particular Java class into Haskell, and what it looks like as we add functionality to both versions of the code.

Haskell evangelism. I’ve already written about the things in Haskell that I love. I love Haskell, and I use it to make my dreams come true. On the other hand, I’m not at all invested in whether or not you like Haskell. Though if you want to learn more about this language, I’d like to help you along your journey.

What I am going to talk about is how to use monads to do something in Haskell that is easy to do in Java.

Some idiomatic Java code and some non-idiomatic Haskell code

Let’s start by taking a look at a class in Java:



public class IntWrapper { private int m_i; public IntWrapper(int i) { m_i = i; } public void print() { System.out.println(m_i); } public void inc() { m_i++; } public void nextPrime() { while (!isPrime()) inc(); } public boolean isPrime() { for (int t = 2; t < m_i; t++) if (m_i % t == 0) return false; return true; } }



We’re going to re-implement this in Haskell using monads. To do this, we’re going to need to look at some facets of this code that are so obvious we usually wouldn’t even think to mention them:

( Valid by construction ) This class has a constructor. The constructor is there to make sure that the class methods have valid data to work with.

) This class has a constructor. The constructor is there to make sure that the class methods have valid data to work with. ( Data hiding ) Once we’ve created a new IntWrapper object there is no way to get its private member m_i out: the only code that can access this member variable are the member methods.

) Once we’ve created a new object there is no way to get its private member out: the only code that can access this member variable are the member methods. ( Implicit scoping ) Member methods can refer to m_i without specifying an object instance. This is because the variable m_i is implicitly this.m_i .

) Member methods can refer to without specifying an object instance. This is because the variable is implicitly . (Encapsulation) Member methods can refer to m_i belonging to other instances, but methods in other classes (that is, methods not in the IntWrapper class) cannot do this.

How might a translation of this class look in Haskell? Here’s a “day one” approach:



module IntWrapper (intWrapper, printiw, inc, isPrime, nextPrime) where data IntWrapper = IntWrapper Integer -- We don't export the IntWrapper constructor because we want to -- preserve encapsulation. Because of this, we need to export a -- function that wraps the constructor. That's what intWrapper does. intWrapper i = IntWrapper i printiw (IntWrapper i) = putStrLn (show i) inc (IntWrapper i) = IntWrapper (i+1) isPrime (IntWrapper i) = isPrime' 2 i where isPrime' t i | t >= i = True | i `mod` t == 0 = False | otherwise = isPrime' (t+1) i nextPrime iw = until isPrime inc iw



What makes this a “day one” approach? It’s all about the interface. Let’s do a compare and contrast between using the IntWrapper class in Java versus the IntWrapper data type in Haskell.

Java:



public class UseIntWrapper { public static void main(String[] argv) { IntWrapper myInt = new IntWrapper(20); myInt.print(); for (int i = 0; i < 4; i++) myInt.inc(); myInt.print(); myInt.nextPrime(); myInt.print(); } }

Here’s what this might look like in Haskell (again, a “day one” translation):



main = do let iw1 = intWrapper 20 printiw iw1 let iw2 = (iterate inc iw1) !! 4 printiw iw2 let iw3 = nextPrime iw2 printiw iw3

This is utterly terrible. Because we aren’t mutating any of our variables, we’ve been forced to define three variables ( iw1, iw2, iw3 ) to try and describe a succession of modified values. It’s obviously error-prone. In fact, when I did this translation, I typo’d my way into print iw2 for the last line instead of print iw3 (seriously). If you like this style, that’s cool, but if you want to tell me that I should like it as well, be prepared to get punched in the face.

The essential weakness of this style is that it requires us to manually thread the IntWrapper data through each of the functions. The Java code also does this (we need to refer to myInt throughout) but clearly Java’s support for mutable state means it isn’t as ugly as our Haskell code.

The difference between the two

Isn’t this an example of why Java has better syntax than Haskell? Well, no: our Java code looks better than our Haskell code because we have used good Java practices and bad Haskell practices.

It’s quite easy to write equally crappy code in Java. In fact, let’s do just that. When we’re done, we’ll compare the bad Java to the good Java, make a few observations, and apply those lessons to our Haskell code.

Here is the IntWrapper class, written so that

The m_i variable is final, thereby simulating the way in which Haskell prevents us from mutating variables, and each of the methods will be static, thereby simulating the way in which our Haskell functions led us to have iw1, iw2, iw3 in our code.

Here is the resulting mess:



// IntWrapper2.java public class IntWrapper2 { private final int m_i; public IntWrapper2(int i) { m_i = i; } public static void print(IntWrapper2 iw) { System.out.println(iw.m_i); } public static IntWrapper2 inc(IntWrapper2 iw) { return new IntWrapper2( iw.m_i + 1 ); } public static IntWrapper2 nextPrime(IntWrapper2 iw) { int i = iw.m_i; while (!isPrimeHelper(i)) i++; return new IntWrapper2(i); } public static boolean isPrime(IntWrapper2 iw) { return isPrimeHelper(iw.m_i); } private static boolean isPrimeHelper(int i) { for (int t = 2; t < i; t++) if (i % t == 0) return false; return true; } } // UseIntWrapper2.java public class UseIntWrapper2 { public static void main(String[] argv) { IntWrapper2 iw1 = new IntWrapper2(20); IntWrapper2.print( iw1 ); IntWrapper2 iw2 = iw1; for (int i = 0; i < 4; i++) iw2 = IntWrapper2.inc(iw2); IntWrapper2.print( iw2 ); IntWrapper2 iw3 = IntWrapper2.nextPrime(iw2); IntWrapper2.print(iw3); } }

Where we had originally made use of objects to pass state between function, in this crap code we are not doing any such thing. It looks strikingly similar to our Haskell code, but with extra verbosity (obvious fact: this isn’t an argument against Java, because in practice no one codes like this). Let’s analyze the differences between this crappy implementation and our original Java code.

An analogy and a lesson

In our original Java code, the IntWrapper class has a mutable member variable m_i . Our client code could then create a new IntWrapper , call it myInt , and repeatedly use the same variable as it did its calculations.

I want you to think of the line IntWrapper myInt = new IntWrapper(20) as a declaration of a context. It is saying “hey, make me a new context for carrying around an integer. Call it myInt .” Then I want you to read lines like myInt.print() as saying “go into the myInt context and print .” This is precisely what this code means. In other words, to some approximation, classes in Java provide us a way to define a stateful context.

This mode of thinking is important enough in what follows that I want to expand on it a bit. Let’s leave the world of programming for a moment and enter into an analogy. You and Sue are both talking about a mutual friend named Bob. You’ve both known Bob for years. Your conversation might look something like this:

Sue: Did you see the postcard Bob sent me from Alaska?

You: Yeah. I couldn’t believe what he wrote on the back!

Sue: Oh, the thing about the reindeer? That’s an old inside joke we had from when we were dating.

You: I didn’t know you guys had dated.

Sue: It was before we met. We broke up because he wanted kids and I didn’t.

You: Really? I never figured him to be the fatherhood type.

In this exchange there are a few things to observe:

After Sue mentions Bob in the first line, for the rest of the conversation he is only mentioned by pronoun. The conversation implicitly carries the information that you’re talking about Bob, and indeed, it appears to carry the information about which Bob you’re talking about.

Bob you’re talking about. Sue and Bob have their own contextual information (the inside joke) that wasn’t known to you until Sue alluded to it.

For that matter, the information that the two of them had dated wasn’t known to you until Sue mentioned it.

Before the end of the conversation, your mental model of Bob has shifted: you now know that he wants to be a father, whereas before you did not.

Structurally, when the conversation started, it was established that you and Sue are working in a context where “Bob” is a known entity. This is analogous to the Java statement IntWrapper myInt = new IntWrapper(20); which establishes myInt as a known entity. As Sue reveals information about Bob, changing your mental model of who he is (his state), she is speaking statements similar to myInt.nextPrime() .

In our crappy Java code, we’ve basically abandoned this whole “context” idea.

So to fix our Haskell code, we’re going to do what we already were trained to do in Java: introduce a notion of context. To some approximation, this is what monads are about. A subtle point is that there are different notions of context that arise in programming, and different monads for modeling each. Following our conversation example from above, this is already something you are familiar with: some conversations are friendly, some are professional, some are adversarial, etc, and each carries with it a unique set of rules for what’s appropriate and what is not.

This is what different monads do: each comes with its own set of operations that are legal within the context that the monad is modeling. In some cases — as with some conversations — it becomes necessary to nest contexts. This is what monad transformers do: they give us a way to define some nested contexts and to move information back and forth through the nesting. Again, an example conversation to illustrate the point:

You: Say, have you heard any news about Bob’s mother?

Sue: Yeah, I heard she’s doing alright.

You: Has Bob gotten over the fact that she’s dating so soon?

Sue: Last time I brought it up he changed the subject.

You: Did she mention anything about whether or not she can make it to Bob’s surprise party?

Sue: She said she could if we schedule it for Saturday, but not if we are shooting for Sunday.

Here the conversation enters a sub-context dealing with Bob’s mother. Information about Bob’s relationship with his mother is moved around, based on the fact that there are two related contexts now in play in the conversation. Monad transformers provide a tool for moving information between nested contexts as well.

In Java, the analogous situation is where you have an object which references members that are also objects. This is obviously an extremely common situation. Later, when we add logging to our examples, we’ll see this explicitly.

Enough analogies, time to be concrete

We will add some context to our Haskell code via the StateT monad transformer. You can think of StateT as a design pattern for when you think your code would be simplified if you had some mutable state. (Unsurprisingly, this is a common thing.) I frequently see people claim that Haskell doesn’t support mutable state; we’re about to see that this is false.

The StateT monad gives us a single mutable variable. It gives us three functions for working with this: put , get , and modify (the latter is a convenience function built as a combination of put and get ). Refactoring our Haskell code to use StateT will complicate it a bit, but ultimately boils down to two types of changes:

Whereas our earlier Haskell code didn’t have any type annotations, once we start using StateT it will be better if we include some. The reason is that, while Haskell is clever enough to infer that we are using some monad, it isn’t always clever enough to infer which monad we’re using. (This is often related to the monomorphism restriction, but that’s a topic for another day.) The type annotations that we’ll be putting on will be pretty bland.

it will be better if we include some. The reason is that, while Haskell is clever enough to infer that we are using monad, it isn’t always clever enough to infer monad we’re using. (This is often related to the monomorphism restriction, but that’s a topic for another day.) The type annotations that we’ll be putting on will be pretty bland. We’ll use some do notation, which will make our code look more imperative. We’ll also use get and modify .

There’s one other change that we’ll make: we’re going to replace our intWrapper constructor with a different style of constructor that will wind up exposing a better interface to our client code.

When you start using StateT , you can think of the functions you write as being very similar to methods in Java: the function implicitly has access to some data (in Java it was m_i , here it is the data accessed by get and put ). The type signatures on these functions documents this fact. It’s important to know, however, that StateT isn’t the same thing as a Java class, due to some differences I’ll mention at the end.

Here’s what the conversion looks like:



module IntWrapper2 (runIntWrapper, printiw, inc, isPrime, nextPrime) where import Control.Monad.State data IntWrapper = IntWrapper Integer runIntWrapper i f = do (a, i) <- runStateT f (IntWrapper i) return a printiw :: MonadIO m => StateT IntWrapper m () printiw = do (IntWrapper i) <- get liftIO $ putStrLn (show i) inc :: Monad m => StateT IntWrapper m () inc = modify (\(IntWrapper i) -> IntWrapper (i+1)) isPrime :: Monad m => StateT IntWrapper m Bool isPrime = do (IntWrapper i) <- get return (isPrime' 2 i) where isPrime' t i | t >= i = True | i `mod` t == 0 = False | otherwise = isPrime' (t+1) i nextPrime :: Monad m => StateT IntWrapper m () nextPrime = do isItPrime <- isPrime if isItPrime then return () else do inc nextPrime

This code has definitely gotten more complex in appearance, but if you pick any given function, the differences between this version and the original are small.

Before we analyze this code, let’s take a look at what effect these changes have had on our client code:



import IntWrapper2 main :: IO () main = runIntWrapper 20 $ do printiw sequence (replicate 4 inc) printiw nextPrime printiw

This is substantially better than our “day one” implementation, and (in my opinion) is even better than our original Java implementation as well. The runIntWrapper function takes two arguments: the initial integer ( 20 ) and a body of code to execute. The body of code (the stuff following the do keyword) is just a sequence of instructions that we want to perform on our IntWrapper . Behind the scenes, runIntWrapper is implicitly threading our IntWrapper to each of these instructions. This really obviates the whole “think of this as declaring a context” idea.

In our conversation analogy, this might be something like sending Bob an email:

To: Bob

From: You

Subject: Directions to my house From I-90, take exit 18 up to the Highlands. From there, take the first right turn, passing by Cafe Ladro. Drive until the road ends. My house is at the very end; park anywhere on the street. The doorbell is broken, so you’ll need to call Sue when you arrive.

The directions don’t say Bob’s name anywhere except for the To field of the email, which establishes that the instructions are implicitly for him.

Some analysis of the transformation

I already mentioned that StateT is a monad transformer. Let’s dig into what this means.

All along I’ve been trying to push the idea that monads provide us with a way of describing “contexts” for our code. When you sit down and try to get some work done with this idea, you’ll quickly run into the following question: how can I nest contexts?

I’ve already mentioned that this is what monad transformers do. Let’s dig into that a bit.

Most monads come in two flavors: a standard flavor and a transformer flavor. The former provides a notion of context; the latter provides a notion of context and is compatible with nesting.

Our printiw function actually demonstrates this idea rather nicely. As a refresher, here’s the code for printiw , exactly as it appears above in our StateT version:



printiw :: MonadIO m => StateT IntWrapper m () printiw = do (IntWrapper i) <- get liftIO $ putStrLn (show i)



Notice that this function does exactly two things:

It uses the get accessor to grab a copy of our integer. It uses putStrLn to print this integer.

What’s so significant about this? In Haskell, all things related to the environment (writing to files, opening sockets, printing to the console, etc) are modeled in the IO monad (read: context). So printiw must be able to work with two contexts: our StateT and IO . The type signature



printiw :: MonadIO m => StateT IntWrapper m ()



documents this: it says that printiw foremost resides in a StateT context, but it must also have access to an IO -capable context m (that’s what the MonadIO m assumptions means). The line liftIO $ putStrLn (show i) does two things:

It indicates that we want to print to the console. Trouble is, printing to the console is an action in the IO context, and our code resides within the StateT context. So to fix this… it then takes this action and “lifts” it to the broader ( StateT ) context (via liftIO ).

Note to the reader. If this seems like boilerplate, it’s because it is. Lifting is a facet of the monad design pattern that sometimes introduces boilerplate, but is sometimes done automatically (the distinction depends on which monad transformers you’re using, and sadly, it seems to take some experience and trial-and-error to get a sense of when you need to lift explicitly and when it’ll be done for you). In a moment, when we add logging to our example, lifting will be done automatically for us, and our code won’t have any additional lift s in it.

You’ll notice that most of our functions have a signature like Monad m => StateT IntWrapper m () instead of using MonadIO . That’s because printiw is the only function that needs to do an IO action; the others have no such need, so we are able to give them a more general-purpose signature.

Isn’t this a giant hassle?

Experienced programmers know that there are many philosophies when it comes to writing code. Usually these philosophies come in opposing pairs: static versus dynamic typing, functional versus imperative, compiled versus interpreted, etc.

What we’ve just seen is an example of a currently not-so-mainstream philosophy: that types should be used to annotate the ways in which code interacts with contexts. For instance, the type signature for printiw explicitly documents that this function requires access to the IO monad, while the type signature for nextPrime is sufficiently general that we can conclude that this function does not use the IO monad.

There is no related idea in Java or .Net (though the latter is introducing the pure keyword, at present time support is extremely limited). In Java, a function may or may not write to the console; the only way to know is to analyze the code, as the type system isn’t strong enough to make claims one way or the other.

Haskell has a popular reputation for making IO difficult. My experience — and I’d suspect that other experienced Haskell programmers would agree — is that working with IO in Haskell is no hassle at all; the difficulty is in shifting one’s thinking away from Java’s fast-and-loose approach to Haskell’s explicit-by-default approach.

Whether or not this is a good thing is decidedly a matter of one’s programming philosophy.

Lessons in this code that can be reused elsewhere

When I’m programming, I usually start without monads, and add them as it becomes clear that they will simplify my design. Over time, intuition develops that will allow you to decide from the beginning if you want to be using a monad (and which monad to use).

If you want to have some notion of mutated state, StateT is a good way to do it. To take code that is not written for StateT and refactor it so that it is, the basic steps are:

Write a “constructor” function. In our example, that’s runIntWrapper . The constructor function takes the initial value of the internal value (we supplied 20 in our example) and some code to execute within this new context. In terms of implementation, it’s just a wrapper for runStateT , which returns two things: the value of your internal state variable after the given code has executed (in our example we discarded this value) as well as any data returned by the given code. Use get , put , and modify in your functions. Provide some type signatures to help Haskell along.

Obviously this is just a quick and dirty to-do list, and while the steps sometimes vary based on the application, the complexity involved is usually at about this level.

Nesting contexts

Let’s beef up this example a bit, both in Java and in Haskell. We’ll add a logging feature to our wrapper: every time a function is called to act on our wrapper, it’ll make an entry in the log.

In Java the easiest way to introduce logging is to just have a list of strings. Here’s what the code looks like:



public class IntWrapper3 { private int m_i; private List<String> m_log; public IntWrapper3(int i) { m_i = i; m_log = new ArrayList<String>(); } public void print() { m_log.add("print"); System.out.println(m_i); } public void inc() { m_log.add("inc"); m_i++; } public void nextPrime() { m_log.add("nextPrime start"); while (!isPrime()) inc(); m_log.add("nextPrime done"); } public boolean isPrime() { m_log.add("isPrime"); for (int t = 2; t < m_i; t++) if (m_i % t == 0) return false; return true; } public void printLog() { for (Iterator it = m_log.iterator(); it.hasNext(); ) System.out.println( it.next() ); } }

Pretty simple. Here’s how I’d do the same job in Haskell. While I could use my current StateT to carry log data, it would be a bit of a mess; after all, “logging” and “working with IntWrapper ” are orthogonal concerns. Let’s reflect that fact in our code by using another monad transformer in addition to StateT . Folks who have read other monad tutorials will not be surprised when I say that we’re going to use WriterT .

Here’s what it looks like:



module IntWrapper3 (runIntWrapper, printiw, inc, isPrime, nextPrime) where import Control.Monad.State import Control.Monad.Writer data IntWrapper = IntWrapper Integer runIntWrapper i f = do ((a, i), l) <- runWriterT (runStateT f (IntWrapper i)) return (a, l) printiw :: MonadIO m => StateT IntWrapper (WriterT [String] m) () printiw = do tell ["printiw"] (IntWrapper i) <- get liftIO $ putStrLn (show i) inc :: Monad m => StateT IntWrapper (WriterT [String] m) () inc = do tell ["inc"] modify (\(IntWrapper i) -> IntWrapper (i+1)) isPrime :: Monad m => StateT IntWrapper (WriterT [String] m) Bool isPrime = do tell ["isPrime"] (IntWrapper i) <- get return (isPrime' 2 i) where isPrime' t i | t >= i = True | i `mod` t == 0 = False | otherwise = isPrime' (t+1) i nextPrime :: Monad m => StateT IntWrapper (WriterT [String] m) () nextPrime = do tell ["nextPrime start"] nextPrime' tell ["nextPrime done"] where nextPrime' = do isItPrime <- isPrime if isItPrime then return () else do inc nextPrime'

There’s a few basic things to notice. First, signatures like Monad m => StateT IntWrapper m () have become Monad m => StateT IntWrapper (WriterT ["String"] m) () . This indicates that we have a nested context, or in the Haskell terminology, a monad stack. The inner monad (read: context) ( WriterT ) provides us with our logging interface (granted by the tell function). The ["String"] specifies the implementation of our log: we’re just using a list of strings.

Second, we’ve changed runIntWrapper so that it is able to accommodate the logging. In addition to using runStateT , it now also uses runWriterT to get the contents of the log. It is now returning these contents to its caller.

Let’s look at our client code:



main :: IO () main = do (_, l) <- runIntWrapper 20 $ do printiw sequence (replicate 4 inc) printiw nextPrime printiw mapM_ putStrLn l



Not surprisingly, only a little has changed. We are now capturing the data returned by runIntWrapper . The runIntWrapper function yields both the output of our given code (which we ignore with a little _ , as the code we’re giving doesn’t have anything to return) as well as the contents of the log at the end of execution (which we capture as l ). We then print the contents of the log using mapM_ putStrLn l .

A qualitative analysis

There are a lot of important differences between our Haskell and Java code, some we’ve already discussed, some we have not.

One difference is the way in which we separated concerns. In our Haskell code, we wound up using two separate monads ( StateT and WriterT ) to implement our wrapped integer with logging. In our Java code, we used a single class with a member variable (a List<String> ).

In Haskell, using a stack of monad transformers is an idiomatic way to separate concerns. The WriterT transformer is an expert in providing a write-only record; the StateT transformer is an expect in providing a mutable variable. If we were being more serious with our coding, we would have defined a new type like



type IntWrapperT m a = StateT IntWrapper (WriterT [String] m) a



to ease up our type signatures. (This practice also makes it easier to later come along and factor in additional monad transformers. Indeed, defining this type synonym is definitely the preferred way to do things: by leaving it out of the samples I’ve unfortunately demonstrated bad behavior, but I didn’t want to have a sidebar discussion of type synonyms in the middle of the example.)

If we had been more serious on the Java side of things, we could have created a class that was purpose-built for logging. We may have then simply had IntWrapper inherit from this class.

In my view, adding logging in Java and Haskell had about the same amount of overhead (in terms of the work it took as a programmer). Then again, I’ve been working in both languages for years, so adding a member variable or another monad transformer is a pretty routine thing for me. I’d presume that someone who is new to Haskell (or Java for that matter) would have a different view on the issue.

There’s an important topic I’ve thus far ignored: should all Java classes be translated into monads? My experience has been that this is usually a fine way to implement the class’ functionality in Haskell, in situations where the class is used to carry and mutate state.

It’s important to realize, however, that these two approaches to design are not interchangeable. In Java it makes sense to have several instances of a class interacting with one another; in Haskell one usually would not have several blocks of monadic code interacting, unless they were all running within the same monad (read: context).

So while we could implement a method in Java for adding two IntWrapper instances together, a similar function in Haskell would probably require us to refactor a bit (we’d need to look at the MonadPlus typeclass). This would be simple to do, but would have taken us down a different path. This difference does illustrate that Java and Haskell are sufficiently different that a direct translation is not always a natural thing to do.

Of course, there are examples that work the other way as well (that is, monad idioms that are commonplace in Haskell but hard to translate naturally into Java). I wouldn’t say that this is a good way to go about deciding which language is “better,” but it is definitely an example of why Haskell monads and Java classes aren’t really solving the same problems (though clearly their problem domains overlap).

Is the approach I demonstrated in Haskell easy or hard? I believe this to be a matter of one’s experience. I can attest that it took me some time to really grok this approach, but then again, I can also attest that it took me some time before I was able to write good object-oriented code as well.

I think that part of the problem is that, while it’s pretty simple to motivate how one should think about object oriented programming, similar models for how to think about monads have taken some time to develop. I suspect this is a pedagogical problem more than an intrinsic difficulty with the monad design pattern, but ultimately only time will tell.

Using monads in your own code

We’ve just looked at an example using the StateT and WriterT monad transformers. Moving forward, here some important questions:

Will I always use these two monad transformers? Yes and no. When you are writing your own applications and libraries, you’ll probably wind up using these transformers (perhaps as well as ReaderT , which provides a read-only variable) as the foundation for your work. As suggested above, you’ll probably define a type synonym to hide this fact. You’ll frequently have the IO monad at the base (so that you can have IO), but will sometimes use the Identity moand at the base (when you are writing functions that don’t need IO). (The Identity monad literally does nothing. If you have a stack of monad transformers, and need a monad at the base that won’t change the semantics of your program, you can use the Identity monad.) When you use libraries written by others (such as parsec or Template Haskell) you will find that these libraries provide you with their own monads that they want you to use. It will be convenient and natural. You’ll use these monads in a manner similar the client code we were writing earlier. (Parsec, a library for writing parsers, is so natural and intuitive that it’s completely changed my outlook on writing parsers.) Should I always use the transformer versions of the monads, or should I use the “regular” versions? Why use StateT instead of State ? I tend to always use the transformer versions, simply because I find it makes refactoring easier, and it makes the code slightly more general purpose in some situations. If I think I want to use the regular State monad, I often use StateT transformer on top of the Identity monad, yielding the same behavior. Of course, that’s just how I do things. It’s certainly not the law. I’d presume that someone out there will have a very good list of reasons why I’m wrong to do what I just described, and in the interest of being a better programmer, I’d appreciate hearing from them. When should I write my own monad? Aside from creating a type synonym, I rarely find myself writing my own monads (and with a type synonym, you don’t need to define a monad instance anyway). Monads are most frequently used to carry state around, and the StateT , WriterT , and ReaderT monad transformers do that handsomely. Sometimes a need for extra high performance can drive the need to write your own monad. Sometimes you need to write your own monad to interact with some weird Haskell extension (like the rank-2 types example I mention here). Usually, though, you can go pretty far just using the standard monads and transformers. I’ve talked with many multi-year Haskell programmers who have said that they’ve never written their own monad. Your mileage may vary. What are the most important monads for a beginner to know about? This would be StateT , WriterT , ReaderT , and ErrorT . Here’s what they are used for: StateT is used to pass around mutable state, like our IntWrapper example.

is used to pass around mutable state, like our example. WriterT provides an interface for writing to a collection. It is very often used for introducing simple logging to an application.

provides an interface for writing to a collection. It is very often used for introducing simple logging to an application. ReaderT provides a read-only variable. It is most often used to pass around application configuration data in such a way that consumers of this data can’t modify it (which is a useful design invariant in many situations).

provides a read-only variable. It is most often used to pass around application configuration data in such a way that consumers of this data can’t modify it (which is a useful design invariant in many situations). ErrorT provides a way to perform operations which might fail, and to manage failure gracefully. You can think of it as a way to perform computations which might throw exceptions.

From here, if you want to know more about the practice of using monads to get work done in Haskell, I’d suggest checking out Real World Haskell, which has some good discussion about using monads to engineer solutions to real problems, and the Typeclassopedia.