Monads are a wonderful thing, spoiled by being packaged in truly awful terminology.
Monads explained by Eric Lippert
51–58 of 58 posts
Re: Monads explained by Eric Lippert
#52Re: Monads explained by Eric Lippert
#53Earlier 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.
Re: Monads explained by Eric Lippert
#54Re: Monads explained by Eric Lippert
#55The 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
Re: Monads explained by Eric Lippert
#56Do 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…
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 1Re: Monads explained by Eric Lippert
#57Re: Monads explained by Eric Lippert
#58The 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
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.)