5 ms·
The fact that no one is able to show a canonical, succinct example of a monad makes me suspect it's multiple concepts under a single banner. But I haven't spen
by sillysaurus3 12y ago
The fact that no one is able to show a canonical, succinct example of a monad makes me suspect it's multiple concepts under a single banner. But I haven't spent much time trying to understand what one is.
EDIT: Thanks for the responses. I'd love to study any resources that you found useful when learning Monads. Do you know of any?
- deleted 12y ago[deleted]
- eru 12y agoThe mother of all monads is the continuation monad. That's the most canonical monads that can implement all other monads. It's rather too abstract as an example to beginners, though.
- noelwelsh 12y agoThe thing that makes monads so powerful is they can be used in a such a wide variety of situations. You can model control flow with the continuation monad, concurrency with the future monad, and error handling with the either monad. Three very different domains but only one abstraction to learn! I can also give you a very succinct definition of a monad, or a succinct example, but I doubt you'd understand them until you've seen many examples in different domains. (If you want examples or defn, there are many web pages that have them.)
- edwintorok 12y agoWhat helped me understand monads was starting from the notion of 'promise' (or future), and how you can chain those: https://en.wikipedia.org/wiki/Futures_and_promises https://en.wikipedia.org/wiki/Futures_and_promises. Once you have that concept it is easier to now learn what a monad is in the scope of a monadic concurrency library. Similarly learning about the either monad might be easier if you start from the concept of chaining error handling, as in this tutorial: http://fsharpforfunandprofit.com/posts/recipe-part2/ http://fsharpforfunandprofit.com/posts/recipe-part2/, and then show how it can be done with monads. But I agree with the 'monad tutorial fallacy' that you'll only learn it once you actually try to write code using it and see if it works the way you thought it would.
- dllthomas 12y agoThere are a large number of canonical, succinct examples of monads. Are you interested in use or definition?
- stormbrew 12y agoThere are clearly a bunch of examples of monads that are even pretty understandable in contexts that aren't Haskel (Maybe and Either, as this blog post points out, are succinct examples). It's multiple concepts under the same banner in the same way that 'the visitor pattern' can represent a whole bunch of actual tasks, but the way you perform them is similar enough to justify a name for it. Really the problem, alluded to in the blog post, is that the worst thing you can possibly do to 'explain' monads is to try to remove them from their context. They aren't fruit, they aren't tupperwear, and a bunch of the incarnations of them aren't useful in imperative programming, so trying to explain them in those contexts is futile.
- logicallee 12y agoIn case you didn't notice, the blog post doesn't bother to explain Maybe or Either. In the terminology of the blog post, this is like asking what a musical instrument technically is, and being told to go learn the piano and violin - from someone else, no help here - and stop bothering the busy blogger.
- stormbrew 12y agoTo be fair, I don't think the blog post is even attempting to explain what a monad is. Its audience is the people who keep making misguided posts that attempt to do so badly. Sure, maybe that's a little unfairly meta if you really want to know, but it is what it is.
- logicallee 12y agoYes, my criticism is that the blog post does not even attempt to explain, or teach, what a monad is. Which would be a primary reason to click on it.
- kqr 12y agoHello, author here. As I hint to in this article, explaining or teaching "what a monad is" is fallacious. Given that each monad requires 400–2000 words to teach in its most basic form, explaining the most common monads in a single article would make that article around 15 pages long. That is way out of scope of this article, but perhaps suitable for five other separate articles. I'll have to think about it! Meanwhile, I'm sure you can Google search for Stack Overflow questions on Maybe, Either, List and other monad types to get used to them. You can imagine it a little like walking up to a musician and asking, "Will you please teach me the musical instruments?" The answer you get will be filled with counter-questions: "Do you know any musical instrument? What kind of instrument are you interested in? Can you read sheet music?" (Or, for that matter, try asking an OOP programmer, "Teach me the Gang of Four patterns, please!" It will elicit a similar response, not a 15-page blog post. ;) )
- aikah 12y agoI'd say Monads shouldn't require the use of Haskell to be understood. I personally don't understand them.Some people told me it's a bit like "reversed" generics,some others pointed me at promises in Javascript which are "monadic". I guess I don't understand them because I work with languages that make them either hard to implement or totally unnecessary.
- olavk 12y agoF# also has monads. However both Haskell and F# has syntactic sugar which makes the use of monads convenient. Without some syntactic sugar, using mondas requires chains of nested lambdas which quickly becomes convoluted and unreadable. E.g. you could use monads in JavaScript, but it would probably be simpler to solve the same problem without monads.
- eru 12y agoYou can use the chain of nested lambdas for monads in Haskell easily. That's bearable mostly thanks to the way lambda syntax works.
- thinkpad20 12y agoI'm not an expert on promises, but they certainly seem like monads. A promise, if I recall correctly, looks something like this: doThing1().then(function (thing1Result) { doThing2(thing1Result).then(function (thing2Result) { doThing3().then(function() { doThing4(thing2Result + thing1Result).then(function (res) { console.log("I did a lot of things. The answer is: " + res); } } } } Basically, at each stage we're calling a function which may or may not return immediately, and saying "whenever you're done calculating, pass your result into this function I'm giving you here." Similarly, in monadic computation, you sequence a series of "actions" together by passing each a function that determines what to do based on the output of the previous one. In Haskell, the above fake code would be written like this: doThing1 >>= \thing1Result -> doThing2 thing1Result >>= \thing2Result -> doThing3 >>= \() -> doThing4 (thing2Result + thing1Result) >>= \res -> putStrLn ("I did a lot of things. The answer is: " ++ res) So you can see the `>>=` acts like the `then` function we saw before, and each time we're passing in a new function (indicated with a backslash, which resembles a lambda). And since this kind of thing is so common, there's a syntactic sugar for all of this: thing1Result <- doThing1 thing2Result <- doThing2 thing1Result doThing3 res <- doThing4 (thing2Result + thing1Result) putStrLn ("I did a lot of things. The answer is: " ++ res) Hopefully it's simple enough to see the correspondence between the above snippet and the one before it; the two are essentially identical. Now the really cool trick is that the same concept holds up for any monadic computation; that is, any situation in which we have a series of steps, executing in a well-defined order, and at each step we have the ability to "examine" what the previous step did in some way. Thus, for example, we can model a series of fail-able computations, where the failure of any one in the chain should cause the chain to stop at that point; or a series of actions that affect a background state in some way, or a chain of effectful computations like IO, or any number of other things -- the abstract idea tends to pop up all over the place, which is one of the great powers of type classes in general: they give you tools that you can use all over the place, even in vastly different arenas.
- olavk 12y agoEach monads have its own semantics, so yes, they are multiple concepts. The Maybe-monad is used to solve totally different problems than the IO-monad. Also things like list comprenhensions is implemented as a monad in Haskell.
- 21echoes 12y agoI found this to be a great practical introduction to monads: http://codon.com/refactoring-ruby-with-monads http://codon.com/refactoring-ruby-with-monads. It holds your hand the whole way, so it gets a bit long and tedious, but it's great at showing how three different common monads can clean up similar looking code in similar ways. I'm sure the Haskell purists out there can find 20+ different things wrong with it, or will scoff at how it's in Ruby, etc., but if you ask me those are tiny prices to pay in an introductory context for how pragmatic, explicit, and helpful it is.
- kybernetikos 12y agoThere are lots of simple succinct examples of monads, for example Option types and Promises. The point is that monad is more like a design pattern than a 'thing'. This means that it's precisely true that it can cover multiple concepts, but that's a good thing, it's where it gets its value and power from. It provides common patterns of interaction that are applicable in a surprisingly wide range of situations.
- millstone 12y agoI'd say you're right, but it's the extra stuff that Haskell associates with monads that make them hard to learn. Here's how I'd break it down: 1. The three Monad laws, which have been beaten to death. 2. do-notation. You've started to learn Haskell, and you know the let-in syntax for introducing variable bindings, and suddenly you get hit with two NEW syntaxes: the let-but-no-in syntax, and the weird <- stuff. How do these syntaxes relate, and why (if?) are they all necessary? Also you just lost fifteen minutes because that line has a tab instead of spaces, the "last statement must be an expression" nonsense, etc. 3. Monadic APIs, especially IO. You can go through an entire Monad tutorial and be left wondering why you need category theory to get main's argv. Not to mention all of the duplication (mapM, filterM, zipWithM, etc). Tutorials tend to focus on #1, but in my journey I found #2 and #3 to give me the most difficulty.
- barrkel 12y agoImagine a concept called "Additive". Any type that defines a '+' operator is "Additive". In most industrial languages, integers, floats and strings are Additive, because these things support the '+' operator. Monads are at the same level of "concept" as Additive. Something is a monad if it has a set of operators that map to the Monad concept. The level of abstraction you need to be at, mentally, is "supports a specific set of operations with specific rules". A lot like an interface, but at the type level. It is definitely a single concept, but there are variations on it, like Functor and Applicative, which are related. The core concept is adding a layer of indirection between a value and application of a function to the value. What the indirection does is the chief distinguishing feature of the monad. A Maybe monad only applies the function if the value is not None, for example. A List monad applies the function to each value in the list. The IO monad, on the other hand, logically stores the function and keeps it for later - the result of computing a pure functional Haskell program returning IO is an imperative program, which is then executed. That's how Haskell gets its side-effects in a pure fashion.
- eru 12y agoThe Haskellic name for Additive is Monoid.
- wz1000 12y agoThat depends on how you define Additive, whether it includes the concept of commutativity, associativity and so on.
- kqr 12y agoSemigroup is a closer match. Monoid includes an identity element. (All monoids are semigroups, and a lot of semigroups are monoids, but not all of them.)
- barrkel 12y agoI made up "Additive" to introduce the idea of different types all having a single operator in common, rather than as a concrete Haskell-ish thing. Its purpose here is to focus on the right level of abstraction when thinking about what a monad is, particularly when coming from languages like Java, C++, C#, etc.
- TheCoelacanth 12y agoA monad is any pair of functions (usually called `bind`/`>>=` and `return`) that fulfills the three monad laws: 1. bind(return(a), f) = f(a) 2. bind(m, return) = m 3. bind(bind(m, f), g) = bind(m, x -> bind(f(x), g)) That's all. Any pair of functions with those properties is a monad.
- dllthomas 12y agoThat's the abstract. For the concrete, here's the standard pile of examples. Note that all of these have ways to create or interact with them other than the monad interface, but those differ from example to example (often substantially). Monad captures one similarity, there are differences as well. The list monad represents nondeterministic computation ("nondeterministic" in the sense of representing all possibilities, not in the sense of picking a random one). List: return x = [x] bind xs f = concat (map f xs) Maybe represents computation with the possibility of failure. Maybe: return x = Just x bind Nothing _ = Nothing bind (Just x) f = f x State carries some data that can be read and updated in the context of the computation. State: return x = \ s -> (x, s) bind m f = \ s -> case m s of (x, s') -> f x s' A Parser, from parsec, is a bit complicated internally for flexibility and error handling, but the gist is that it represents a parser action that may consume a portion of the input stream and produces a value. Parser: return x: a parser that consumes nothing and produces x bind m f: a parser that runs parser m, passes the output to function f, and runs the resulting parser Functions that all take the same type can be treated as a monad. This is often codified into Reader, to make things clearer. ((->) r): return x = \ _ -> x bind m f = \ r -> f (m r) r And of course famously IO, representing actions that produce a value and may interact with the world at large, forms a monad. IO: return x: an action which does nothing and produces x bind m f: an action which, when executed, executes m, applies f to the value produced, and then executes the result of f
- dllthomas 12y agoSo, for an interesting example of using monads, consider parser combinators. I'm basing this loosely off of the parsers provided by the parsec package, but simplifying a bit. Let the type "Parser a" represent a parser that produces an "a", after consuming some (or no) input. (In Haskell, lowercase letters are used for polymorphic types; Parser Integer is a parser that produces an Integer, Parser String a parser that produces a string, and so on. Any concrete value will be of some more specialized type, but functions can work on the polymorphic version if they don't rely on it being something more specific.) Note that parsers may also fail, and give up on parsing further input, instead returning information about how it failed. To run a parser against some input, we can do something like: case (parse someParser inputString) of ResultFailure errorString -> do something with error string ResultSuccess value -> do something with value where parse :: Parser a -> String -> Result a data Result a = ResultFailure String | ResultSuccess a We can put together a lot of things of this type. The simplest returns just a fixed thing and doesn't even look at the input. return :: a -> Parser a So the expression, parse (return 5) inputString would reduce to ResultSuccess 5, regardless of the input. We can also have things that return the next character, whatever it is: anyChar :: Parser Char parse anyChar "f" -- ResultSuccess 'f' parse anyChar "7" -- ResultSuccess '7' parse anyChar "" -- ResultFailure "premature end of input" Or we can have something more restricted, like... oneOf :: [Char] -> Parser Char abc = oneOf ['a', 'b', 'c'] parse abc "a" -- ResultSuccess 'a' parse abc "d" -- ResultFailure "unexpected 'd'" And we can have more sophisticated things: natural :: Parser Integer So now... where does "monad" come in? Well, when we go to build a new parser out of these components, we need some way of combining them. We can do our parsing explicitly... parseWithLeftovers :: Parser a -> String -> (String, Result a) parseTwoIntegers input = case parseWithLeftovers integer input of (_, ResultFailure errorString) -> ... (input2, ResultSuccess int1) -> case parseWithLeftovers whiteSpace input2 of (_, ResultFailure errorString) -> ... (input3, ResultSuccess _) -> case parseWithLeftovers integer input3 of (_, ResultFailure errorString) -> ... (_, ResultSuccess int2) -> return (int, int2) It should be plenty obvious why we don't want to do that every time. Let's factor out the boiler plate. The question is what that looks like... What if we set it up so those results could be collected and passed to an arbitrary function? That could let us write something like... parseTwoIntegers = (,) <$> integer <*> (whiteSpace *> integer) While you may not understand the symbols, I expect you'll agree that's quite a bit more readable. But that still doesn't actually require monads. <$> is a synonym for fmap, the two operators containing asterisks are from Applicative. Can we do everything we might want with these, without getting crazy explicit and chaining parsers by hand again? Well, let's look a little closer at what's going on above. Specialized for clarity, the above operators have the following types: (,) :: a -> b -> (a, b) tuple operator - takes two arguments, makes a tuple out of them (<$>) :: (a -> b) -> Parser a -> Parser b "fmap" - applies a function to the result of a parser, making it into a parser that does the same thing but returns the modified result (<*>) :: Parser (a -> b) -> Parser a -> Parser b "ap" - takes a parser *producing a function*, and a parser producing a value, and gives us a parser that applies the function to the value (*>) :: Parser a -> Parser b -> Parser b Takes two parsers, runs both, returns the result of the second Walking the above expression from left to right, we see... (,) :: a -> b -> (a, b) -- tuple operator (,) <$> :: Parser a -> Parser (b -> (a, b)) note that the partial application of (,) *inside* the parser gives us a parser returning a function (,) <$> integer :: Parser (b -> (Integer, b)) we've picked our "a" and are left with a parser returning a function - a job for "ap" (,) <$> integer <*> :: Parser b -> Parser (Integer, b) It should be reasonably apparent how the bit in parens reduces, so skipping ahead... (,) <$> integer <*> (whiteSpace *> integer) :: Parser (Integer, Integer) It's easier - and not wrong - to think of "f <$> ... <> ... <> ..." as "apply this function to these wrapped values, giving me a wrapped result" - but the above is how that happens, and following it is a bit necessary for this next bit... What if we want to write readNChars :: Parser [Char] where the parser first reads the number of characters to read, and then reads that number of characters? Looking at the above operators, there's something that the function arguments (wrapped or not) have in common: none of the function arguments return a Parser. So the functions can't dictate what is parsed. That's what we fix next: (>>=) :: Parser a -> (a -> Parser b) -> Parser b "bind" - takes a parser, and a function that returns a parser, runs the first parser, feeds the result into the function, and runs the resulting parser This, then, allows us to say things like: parseCountItems :: Parser a -> Integer -> Parser [a] parseCountItems p i = sequence (genericReplicate i p) readNChars :: Parser [Char] readNChars = integer >>= parseCountItems anyChar Haskell programmers care that it's a monad for three reasons. First, communicative value - the pattern comes up a lot and it's useful to be able to quickly convey what's going on. Second, there are actually a number of useful functions that can be applied to any monad. For instance, mapM :: Monad m => (a -> m b) -> [a] -> m [b] filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] when :: Monad m => Bool -> m () -> m () foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a Finally, do notation. Haskell provides some syntactic sugar that turns do statement1 var <- statement2 statement3 var into statement1 >>= \ _ -> statement2 >>= \ var -> statement3 var The above "readNChars" could be rewritten: readNChars = do cnt <- integer sequence (genericReplicate cnt anyChar) Do notation can often be clearer for complicated monadic expressions.