Live data from Hacker News

Monads explained by Eric Lippert

ericlippert.com

51–58 of 58 posts

Re: Monads explained by Eric Lippert

#51
If you understand function composition, you already understand monads; you just may not realize it yet.

Monads are a wonderful thing, spoiled by being packaged in truly awful terminology.

Re: Monads explained by Eric Lippert

#53

Earlier quoted context omitted.

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.

Re: Monads explained by Eric Lippert

#56

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…

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

Re: Monads explained by Eric Lippert

#58
post #52

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.)

Post reply on HN