Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Monads explained by Eric Lippert (ericlippert.com)
82 points by tucaz on March 5, 2013 | hide | past | favorite | 55 comments


Monads are complicated because the word "monad" bears too much weight in any discussion about them.

1) There is a category theoretical concept of a monad. This is largely irrelevant in a practical explanation of monads since Monads are usually talked about in terms of another category theoretical concept called the Kleisli Category or Kleisli Triple. However, if someone starts talking about "monad laws", they're probably referring to the theoretical sense of this.

2) There is, in Haskell at any rate, a typeclass called the Monad (note the capital "M"). This is mostly important because when a type is an instance of this category, the Haskell do-notation is useful. Aside from that, you can only use the handful of operations that the Monad typeclass implements that you don't have to implement yourself for it to be an instance of Monad.

3) There are Monad type constructors, such as Maybe, IO, and State. This is where things actually start getting interesting for the practitioner, because each provides a specific abstraction of a concept, such as input/output or partial functions (IO and Maybe, respectively).

4) There are Monad types, both abstract and concrete, like Maybe a, IO a, Maybe Int, State MyType and so on. These are the types you actually will program with.

5) There are Monad values, which are instances of the Monad types, which are, themselves, instances of the Monad type constructors, which are instances of the Monad typeclass.

6) Then just to add a slightly extra confusing layer on top of it all, you have the values inside the Monad you'll actually be doing computations with.

7) And finally, it doesn't help when in code examples, type variables and function variables are conflated by name, such as in:

    get :: State s s
    get s = (s,s)
Unfortunately, that's much more clear for an expert than a beginner in regards to what's going on there, so if you're reading code samples, likely written by people with expertise, they're going to be confusing.

So, yeah. Simple concept of explicit effects. Seriously semantically overloaded stack of terms to actual reason with them about.


Minor nitpicks:

"Monadic types" and "Monadic values" would be better for 4 and 5, because "Monad types" are the type constructors (which are also sometimes named types). And "monad values" is not a term I've ever heard used.

Also, values have-type, they're not instance-of. So it would be slightly more accurate to say monadic values are values of a monadic type, whose type constructor is an instance of the Monad type-class.

I agree better and more accurate use of accepted terminology for all these concepts could be useful.

As for the namespacing issue in the examples, it might be confusing for a complete beginner, but one of the first things you learn in Haskell (ideally before Monads) is how scoping works.


I'm sure I've made some terminology decisions that are questionable, but in light of someone writing the community approved guide to talking about monads, I'll try to explain what I was trying to get across.

First, I was trying to emphasize that a type constructor is not strictly a type in the sense of a value having a type. While we commonly speak about to the parameterized types without their type arguments, they aren't really "types" in the type theory sense while there are still type arguments to be supplied. That is, "Maybe"'s kind is * -> * while Maybe a's kind is * even though a is a generic type.

When referring to something as a "monadic value" I was just trying to emphasize that the monad that contains a value is itself a value in the Haskell runtime system and that such a "monadic value" is separate from the value contained there within.

Finally, maybe this is a bad habit of mine, but I've always referred to values as instances since they inhabit types.

I disagree that the namespacing is only ever an issue for a complete beginner. The visual separation between a type expression and a value expression in Haskell can be pretty thin. It's not just about whether they are unambiguous but how much attention you have to pay to while you're skimming code. Honestly, I don't think there's much that can be done about it, but it's just the cherry on top for beginner and intermediate users. Especially ones who aren't using Haskell regularly.


This is beautifully explained.

The obligation to know specialized theoretical concepts like the Kleisli Category might indicate that all this is not much of an ergonomic help for reasoning about code. Making things more conceptually complicated and require more background information is probably not the best way to amplify a person's ability to understand the implications of the code they see in front of them or have just produced.


You don't really need to know anything about the monad laws until you're actually ready to write your own. And you probably aren't going to need to write your own Monads until you're already pretty familiar with how other instances work.

Some people like to explain stuff from the theoretical perspective first. I honestly don't think the theoretical aspect is all that confusing, but it's unfortunately besides the point unless you're writing an article on how and why individual Monads adhere to those laws.


Note that this is a series of posts. The first post that is being linked to doesn't have much IMO.

http://ericlippert.com/2013/02/21/monads-part-one/

http://ericlippert.com/2013/02/25/monads-part-two/

http://ericlippert.com/2013/02/28/monads-part-three/

I wasn't clear for me but in the last post he mentioned that there would be more posts to come.



He sure takes his time..

Episode 4: Conclusion: But we are so close! Next time on FAIC we'll make a small but vital modification to the signature pattern ApplyFunction to arrive at the actual second requirement of the monad pattern.


Eric is attempting to explain it to a wide class of C# developer -- most of whom are pretty unfamiliar with lambdas and higher-order functions, much less eclectic type polymorphism and computation.


Monads should be a familiar concept for the seasoned C# programmer. LINQ's 'from in' syntax and the haskell monad 'do' syntax are almost the same!

Heck, the type signature of SelectMany and (>>=) are even the same!

    (>>=) ::  m a -> (a -> m b) -> m b
and

    public static IEnumerable<TResult> SelectMany<TSource, TResult>(
    	this IEnumerable<TSource> source,
    	Func<TSource, IEnumerable<TResult>> selector
    )

    m = IEnumerable
    b = TResult
    a = TSource

    =>   m<a> SelectMany<a,b>(this m<a> source, Func<a,m<b>> selector)
The type signatures are _EXACTLY_ the same. This isn't a coincidence. LINQ is actually a monad :)

So I think taking this approach to explain the Monad voodoo to a C# programmer is the way to go. He actually uses them already :)


Further proof for my personal conviction that C# is a very interesting, progressive language which masquerades as a Java clone.

I once copied the style of a maybe monad using LINQ - the WinRT apis lack a "FileExists?" method, so I used LINQ to search a directory's contents for a filename. An IEnumerable of size of zero represented None, and size one represented Some.

Unfortunately, a simple try/catch around an attempt to open the desired file was more efficient and clearer to understand.


LINQ has an .Empty() extension method, and you should use that instead of .Count()==0 when determining if an expression is empty.


You should use try/catch; branching would imply a race condition in a multithreaded environment (what if another process deletes the file between the check and the use of the file?).


Likewise, Scala's "for" loop compiles to calls to map and flatMap, which are the same as Select and SelectMany, which represent the list monad. You can use map, flatMap, and for notation in lieu of a more generally named do syntax.


It goes beyond that . LINQ isn't a monad, it's monadic syntax for any monad. It just so happens that the one everyone uses is IEnumerable.

Here, I wrote the Reader monad for IoC to show it could be done:

https://gist.github.com/vmarquez/4640678


LINQ is very similar to the List monad, specifically.


Monads are actually a very simple concept. They're really hard to understand at first because of how much knowledge is prerequisite. I found that by teaching myself Haskell incrementally, in small steps, I was able to understand monads just as easily as anything else. For me, it was simply resolving the following dependency graph:

Algebraic data types -> type classes -> functors -> applicative functors -> monoids -> monads.

I need to emphasise that understanding of each stage is predicated on understanding of the previous stage. Trying to learn monads without understanding these other concepts is like trying to learn Maxwell's equations without an understanding of calculus and physics.

Lippert states: "Plenty of developers have used the monad pattern entirely by accident, not realizing that they're re-inventing something that already has a name.". This is completely true. For instance, watch this video on refactoring by Ben Orenstein [1]. He 'destroy[s] conditionals with a NullObject'. This is exactly how the maybe monad works.

[1] http://www.youtube.com/watch?v=DC-pQPq0acs


Learn You A Haskell teaches precisely in that order and I've found it worked great for me. I was never confused and at every step it was obvious why the concepts were useful.


I'm beginning to think that all the people who have ever understood monads have written an article trying to explain them.


I understand monads and use them everyday, but I've never written an article about them. I did once try to explain to someone what a monad is... that could be the reason I've never tried.


I understand monads and use them everyday,

Same thing here and I am not writing a tutorial, because good tutorials already exist. For example:

http://blog.sigfpe.com/2006/08/you-could-have-invented-monad... http://www.manning.com/bjarnason/

In fact, the latter book lets you write your own functors and monads without even telling you, and then shows you that you actually already understand these concepts.


You should write an article!


I'll start with the continuation monad, explaining that via the Curry-Howard correspondence it is related to a double negative. I'll go on to show that the continuation monad is more general than any other monad by showing how to implement some common ones (state, list, IO, coroutines) using it. I'll finish with a half-baked analogy.


Write a program which generates explanations of monads. By the time Haskell compiles it, it will be provably correct without the need for tests.


Only three kinds of people have ever understood Monads - some of them died, others went mad trying to formulate explanations and the rest have forgotten all about them.


And that somehow in understanding monads, the lose the capability of explaining them.


Do you know what I miss about these explanations?

Technical people can't go straight to the point. They go around, around, around, going into corners, etc

(not only on explanations, mind you)

For example, math has some very high-level/generic explanations, and curiously, I find them more understandable.

So, I've given up on understanding Monads (or at least Haskell ones), I'll probably understand them when I come up with a similar problem and think about how to solve them.


What do each of these equal?

  1) Just 1 >>= \_ -> Just 2

  2) Just 1 >>= \_ -> Nothing

  3) Just 1 >>= \_ -> Nothing >>= \_ -> Just 2

  4) Just 1 >>= \x -> Just(x+1)

  5) Nothing >>= \x -> Just(x+1)

  6) Just 1 >>= \x -> Just(x+1) >>= \x -> Just(x+1)

  7) Just 1 >>= \x -> Just(x+1) >>= \y -> Just(x)
If you can answer all of those correctly, you should have a decent understanding of monads.

answers:

  1) Just 2

  2) Nothing

  3) Nothing

  4) Just 2

  5) Nothing

  6) Just 3

  7) Just 1


In this case, going around and around really helped me grasp the concept. Every other monad tutorial out there always go straight to the point and that is what made it difficult for me to understand them. I really like this series so far and I think this is the perfect way to explain something that complex. "You need to know where you came from to understand where you're going".


"For example, math has some very high-level/generic explanations, and curiously, I find them more understandable."

That's because math always "goes straight to the point". There's nothing but the raw substance in it.


When I read math, I really like some motivating examples before digging into the definitions and theorems. For me, concrete examples makes it way easier to grasp abstract concepts.


Oh, I agree, sometimes it's too generic =)

Especially if you don't know the names used in the description.

The problem is that I can't stand explanations that would go like this if it was explaining Math:

"So, we have a natural number, that is 2, 7, or 34232345 and by the way they're all integer numbers as well, also they are rational numbers as well, oh and there's equation you can do with them, but ok, so you can sum those numbers, etc"

I'd rather go with

"This turns the natural numbers (N, +) into a commutative monoid with identity element 0,"


I find the best analogy is with optimized code. It's not like you're deliberately obfuscating it, but still, in trying to write it as succinctly as possible, some of the information has gotten lost.


Learn You A Haskell describes it quite clearly and with minimal analogies. You could give it a try.


I agree, monad explanations that use analogies are bad. There is no need for analogy, it is a simple concept, and is easiest to learn from an explanation of the actual concept, not semi-accurate sometimesish analogies.

I think this is the best monad introduction I've seen: http://blog.sigfpe.com/2006/08/you-could-have-invented-monad... It shows you why monads are a thing, what kinds of problems they can be used to solve, and then you see the overall pattern of how those different problems share the same underlying pattern (they are monads).


I tried to like this series. I really did. And it starts out so strong...

The thing is, you can't escape talking about the specific implementation details of monads while talking about monads, otherwise you lose the plot of what we're doing and why. Saying, "Monads are just code patterns" is like saying, "All matter in the universe is just energy patterns," strictly correct but unhelpful for any true insight.

Its okay to skip the monad laws at first, but what's not okay is to talk about why there are so many Monads or what actually constitutes a monad. The best writing I've ever seen on this subject is from Learn You A Haskell for Great Good, where they walk you through a piece of code and then show how using a monad makes it cleaner and less tedious.

By the end of those 2 chapters from LYAH, you end up with the sense that monads are obvious and natural. And they are once you understand the sorts of problems they solve. I wish we had more of that.


As a C# developer, here's the explanation that made it all crystal clear to me: http://stackoverflow.com/a/2484101/80112


Why do you need to understand monads? Nobody explains groups to people using integers, either.


Agreed. Just a couple days ago I was having a discussion with someone about how Haskell's type system is able to quarantine effects (he's a long time C++ and Python user who wanted some help seeing how a type system could possibly do that). I managed to explain it without ever using the "M word".

Instead, I explained that "functions" in Haskell are deterministic and that "procedures" have a different type from functions. From there it was pretty easy to add that there are also types that represent more-restricted kinds of procedures that only have access to a limited set of effects. If he ever decides to take the plunge and actually learn some Haskell, I'll connect the idea up to the Monad class as "the minimal interface common to all procedure types", with emphasis on the idea that it's just the minimal API necessary to make something like "do" notation work.

That is really all a user needs to know about monads to get started with using them. Of course they need more if they'd like to implement their own, but by the time they're thinking along those lines most people will have already learned enough about them from osmosis since, as other people have remarked, it's not a hard concept - it just has a lot of prerequisites. Even then, though, most of what most users want is well enough served by a similarly shallow understanding of monad transformers (they add features to procedure types, etc.)


You've never been curious about something you don't know about, but don't immediately need?


Whether any particular tutorial is good or not, it's almost automatic that people (especially in the Haskell community) question whether we needed /yet another/, because there are already so many and most of them are not that great.

IMO there's nothing wrong with more, but the fact that there are so many out there seems to perpetuate the incorrect belief that it's something you need to learn to actually use Haskell. I suspect that the GP was addressing this belief, not saying "nobody should bother learning this stuff".


In general the Haskell community seems way too obsessed about monads. I just checked the "official" tutorials [0]. I was happy to see they all introduce IO without talking about monads. However, the linked wiki page has a whole section about monads. In contrast, there nothing about concurrency or transactional memory, despite that fact that this is one of the strong parts of Haskell.

http://www.haskell.org/haskellwiki/Tutorials


>In general the Haskell community seems way too obsessed about monads

You need to understand monads to do anything beyond trivial exercises. It is something that virtually every single person coming to haskell from another language is unfamiliar with. I don't see how a focus on such a fundamental aspect of the language is a bad thing.


@DigitalJack's comment [http://news.ycombinator.com/item?id=5325542] reminded me of another talk on monads viewed by me recently, by none other than dear ol' Dougie!

http://www.yuiblog.com/blog/2012/12/13/yuiconf-2012-talk-dou...


I've also (sorry!) written a blog about understanding monads as a design pattern http://kybernetikos.com/2012/07/10/design-pattern-wrapper-wi... It was motivated after I'd written a simple promises library and suddenly realised that promises are monads too.


The simplest explanation of Monads I've ever read (as a Python developer) was actually one in a comment by a Hacker News user 'frio':

http://news.ycombinator.com/item?id=4960701


That's a decent explanation that covers part of what monads are, although since it uses Python, it cannot express the type goodness that comes with monads in languages such as Haskell and, more recently, Rust.

Both Haskell and Rust exploit compile-time typing to avoid having null (or nil, or None, or whatever) pointers in the language -- something which eliminates an entire class of errors that plague most of today's languages, including Python.

The reason they can do this is that with Maybe, the wrapped value is either something (Just a) or nothing (Nothing). Since Nothing is a type (more technically a type constructor), not a value -- unlike something like Python's None, or C++'s null -- it cannot be passed as the value to a function which expects a valid pointer to a different type:

    -- This won't work
    setUserName(user, Nothing)
Another thing about Haskell is that the IO system relies on monads for a very specific reason, or maybe two reasons. Since Haskell is lazy and pure, it needs a mechanism to express the fact that a function has actually done some work, even though -- from the compiler's point of view -- it hasn't. For example, consider a function call such as:

    putStrLn "Hello world"
Well, this function has nothing particular to return; in most languages it would be a "void" function or return a null value. But this would not work in Haskell, which is designed to (1) optimizes function calls that return the same value (so if you call this more than once, Haskell would normally just evaluate it once), and (2) completely eliminate function calls whose values are not used.

For this reason, Haskell's putStrLn has to return a unique value every time it's run, just to convince the compiler that it should not be optimized away. And so it does -- this is the IO monad. putStrLn returns a new IO value for every call. Since it always returns a new value, this ensures that Haskell won't optimize it away.

There is another aspect to the IO monad: Haskell being lazy and pure, it's allowed to evaluate everything out of order. The way the IO monad is passed, baton-style, between each operation ensures that the compiler is forced to evaluate I/O operations in the sequence they appear in the code. Well, mostly. You still have to be careful; you can easily end up in a situation where a read is only evaluated after you closed a file, for example.

(I'm not a Haskell expert, so I would be happy to be corrected on any point. Note I have glossed over some technical details in my explanation that would just complicate things.)


That is an incorrect explanation fyi...


Dammit, how wrong?


The monad is the clitoris of programming.


One of these things is routinely found by most humans, and the finding can be taught to an idiot in one or two sentences.

If you are going to make an improper analogy for monads, it should probably be the prostate or the g-spot.


What is that supposed to mean ?


I took it as a reference to the clitoris being "Nature's Rubik's Cube" --- the most difficult puzzle in the world. Pretty funny, actually.


I think he is high, all those recent drug related posts ya know...


I need On-Star ...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: