> You didn’t learn python via abstract tutorial, comparing lists to Smaug and dicts to Bilbo [...]
> STFU and code. There is no other way to learn to code.
I think this is true of a lot of languages, but learning Haskell verges on learning mathematical concepts at times and learning monads is definitely one of those times. In these cases analogies are sometimes the easiest way to grok the concepts.
I don't think this is necessarily true. I'm fairly strong in math, and definitively enjoy approaching problems in the abstract. However, even in math, it is often the case that by using something you gain an intuition about how that thing behaves.
Monads defiantly worked that way for me. I am good enough at math that I could look at their definition and 'understand' what they are doing (it really isn't complicated, its just difficult to explain). However, when it came time to use them in code I had almost no intuition about how they should behave. Now, my understanding of them is (in my opinion) not at all the natural understanding that would arrive from their definition, but an understanding that is accurate, and far more useful in code.
So, maybe I'm just being overly provincial here--I rather suspect I am--but does anybody else wish people could explain, to a three year old, why anyone should give a fuck about monads?
They always seem to come up along with Haskell, and as far as I can tell they seem mainly to be CS hipsterism.
So, pretend I'm a three-year old...or even better, pretend I'm a grumpy old C programmer.
Can anyone here (please!) explain to me:
1. What are monads (using grade-school language)?
2. What does a monad in C look like (or C++, if you need template magic or OO stuff)?
3. When will I use one outside of my Haskell class or Google interview?
EDIT: I gather that monads are some kind of weird thingy related to doing proper function composition in type systems that would otherwise get in the way, but maybe I'm smoking crack?
I don't feel like adding to the "what is a monad" literature. However, there was a lively discussion on LtU recently regarding this after a posting about Joe Armstrong's Three Laws of Programming Language Design [1]. They were brought up because one of the rules was
What is difficult to understand you have to explain to people over and over again.
The difficult to understand stuff is a real bummer. You have to explain it over and over again until you’re sick, and some people never get it, you have to write hundred of mails and thousands of words explaining over and over again why this stuff means and why it so. For a language designer, or author, this is a pain in the bottom.
More than one person brought up monads in relation to the above quotation. The comments there are fairly short and make an attempt to be both precise and motivated. This comment [2], along with its first reply is closest to addressing all three of your questions.
The longer approach, which I personally found descriptive, is chapters 11-13 of Learn You a Haskell for Great Good! [3]. This is by no means the succinct description that you are looking for, but it does a good job at motivating monads by bringing about functors and applicative functors as solutions to a certain problem, and then showing how "moands...are just beefed up applicative functors, much like applicative functors are only beefed up functors."
Just imagine C gave you the power to overload the semicolon operator. You could now write a series of expressions (separated by semicolons) to express other concepts (such as non-determinism, failure or state). This is scratching at the surface of what a monad does. It effectively allows you to define a context (with its own semantics) and gives you a set of powerful (overloaded) operators for working with values inside that context.
In Haskell, side effects are constrained to the IO monad. This allows one to see, at a glance, whether or not a particular function has the ability to perform side effects. In C, on the other hand, it could be said that all functions live in the IO monad and that no other monads exist. This makes it harder to reason about the behaviour of a function, as one does not know whether or not it will perform side effects.
Disclaimer: I'm not a Haskell hipster and I personally think that people trying to advocate that language and FP in general do themselves a huge disservice by focusing so heavily on monads and other type calculus concepts in introductory contexts.
1. Monads are a means to create ordered evaluation in a system that does not guarantee it as part of its execution model. You wrap actions in other actions so that the act of unpeeling them results in computation occurring in a predictable order. (not grade-school, but I do think it's more understandable to a non-FP aficionado)
2. They don't. Things always happen in a predictable sequence in C/C++.
Ah, so the main utility of them is helping to make ordering guarantees in a language that would normally be more about mapping data points to their images under a function?
I can take a stab at this, but someone else might be more comprehensive.
1) Grade-school language is too hard. I am not a mathematician, though, nor a hardcore Haskeller. But maybe that's good enough. Let's see.
A Monad is typically a parametric datatype which stores a value with some context. (A "parametric datatype" is just a concise way of describing something like scoped_ptr<Foo>.)
An example would be Maybe. A Maybe is a parametric type, much like your scoped_ptr<>. An example would be Maybe Int.
A Maybe Int can be one of two things: Nothing, or Just x, where x is any Integer. Examples: Just 2, Just -8, Just 0.
Monads support two functions: bind and return. Maybe is no exception to this.
Return (unfortunate name) puts a value inside a monad, in a minimal context. So if you apply "return" to the number 6, you get Just 6.
Notice that unless you implement your own arithmetic routines, you can't (hand-wave) use operations like + or - on a Maybe Int. This is what the second operation, bind, is for.
Bind applies a function to the value inside the monad, with the stipulation that the function in question itself results in a monad. (Hold on to that thought.)
Bind is what you'd use to add (e.g.) 3 to Just 6, or how we'd divide Just 9 by 3.
Why would Nothing be useful? Well, in this dumb example, you might want to implement a division operator. So what if you have Just 0 and attempts to divide it by 3? Well, since Nothing is a valid value for Maybe Int, you could just return that instead of throwing an exception.
By convention many functions which can result in a failed computation return nothing. So a key lookup in a map, for instance, will return Maybe MyVal. It's incumbent on the caller to check for whether it's an instance of Nothing or Just MyVal; there's no "null" in the picture. (I'm hand-waving here; there's a ton of syntactic stuff in Haskell to make this very simple.)
I'm not going to talk about function composition, except to say that Bind lends itself well to that; you can bind multiple items expecting such as a Maybe Int in a row, since a stipulation of bind is the result has to be a monad.
2) They're probably doable in C++ with template magic, but I would never ask someone to subject themselves to that.
3) You won't necessarily use them but they are a useful concept to have in mind. Maybe itself crops up in such as Scala or other places, as an Option type.
It's conceivable that instead of returning null/None or the moral equivalent you design your classes such that nobody returns null. Instead you might return an empty Person class. Calls on it which do nothing could be a no-op instead of crashing the program, or what have you; you can define what behavior you want to occur rather than a crash/stack trace.
I probably didn't do a good job explaining this but I hope it helps. I tried to define monads in a more operational way rather than explaining the theory, which I myself can't explain in a systematic or comprehensive way.
1. There is probably no faithful grade-school language explanation. Monads are generally the composite of a left adjoint functor followed by its right adjoint. Adjoint functor pairs often consist of a free and a forgetful functor. A forgetful functor maps objects from some category to similar objects with less structure. E.g., groups to their underlying sets, abelian groups to groups, groups to monoids, etc.
A free functor gives objects with less structure the "minimum possible" stuff needed to be an object with more structure. Importantly it doesn't assume anything about the simpler object. That is, the free construction should work in the same way for all objects of the same kind. For example, we might want to construct the "free monoid" from a set. A monoid has an associative binary operation and an identity element. The most natural way to make a monoid out of the set is by making the elements of the monoid strings of elements of the set, with string concatenation as the operation and the empty string as the identity element.
Composing a free functor with a forgetful functor means we're taking some object, giving it some generic properties, and then "forgetting" what kind of properties it was given. Forgetting is in scare quotes here because a lot of the information about what these properties are like is captured in two natural transformations, `return' and `join'. (I won't attempt to explain what those are. If you have some experience with functional languages, they're a lot like polymorphic functions. E.g., a function that takes a type `A' to the type `List A'.)
Say F is a free functor and G is a forgetful functor. T = G . F is a monad. For some object X in our less-structured category, `return' expresses a correspondence between X and T X. `join' expresses a correspondence between `T (T X)' and `T X' (the basic idea here being that adding free structure twice is similar to adding it once). This is obviously a pretty incomplete (and judging from your request, too mathematical) treatment, but there is no such thing as a faithful non-mathematical treatment of monads. They are mathematical objects.
One neat things about monads in practice is that they're typically pretty useful. Go out and find an arbitrary adjunction, and it's likely that it leads to a useful and/or interesting monad. One fun one is (s,-) and (s ->) which leads to the state monad.
2. You can't really express monads in C and C++. The necessary constructs to talk about these mathematical objects are just not there.
3. If you use Haskell regularly, you'll use them all the time, even outside of your class. They are a useful way of doing things like passing around state or expressing safe exceptions. I doubt Google would ever ask you about something like monads in an interview.
Sorry I can't give you a better idea. The best way to learn about monads is to use them and study the relevant category theory.
> STFU and code. There is no other way to learn to code.
I think this is true of a lot of languages, but learning Haskell verges on learning mathematical concepts at times and learning monads is definitely one of those times. In these cases analogies are sometimes the easiest way to grok the concepts.