Monads explained by Eric Lippert
ericlippert.com
ericlippert.com
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.
"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.
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.
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.
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.
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.
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.
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 :)
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.
Here, I wrote the Reader monad for IoC to show it could be done:
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.
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.
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.
That's because math always "goes straight to the point". There's nothing but the raw substance in it.
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 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).
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 1The 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.
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".
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.
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.)
http://www.yuiblog.com/blog/2012/12/13/yuiconf-2012-talk-dou...
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.)
If you are going to make an improper analogy for monads, it should probably be the prostate or the g-spot.