15

I've read a lot of posts that explain what monads are, how unit and bind work, some of them plunging straight into category theory so abstract (for me at least) that makes the eyes bleed, some ignoring that altogether and touching on weird analogies of burritos, boxes and what not.

After a few weeks of study and a lot of fried neurons, (I think) I understand how Monads work. But there is still one thing that escapes my understanding, something that few posts actually touch on (except for IO and state):

WHY?

Why do Monads matter? Why are they so important? What's the problems they are solving? Can those problems only be solved with Monads or are there some other ways?

Dummy Me
  • 195
  • 3
  • 3
    Programs are big and complex. We need ways to structure them. There are many ways. Monads are one. Arrows are one. Functors are one. Objects, Classes, Functions, Methods, Modules, Traits, Mixins, Packages, are some others, It's really not clear what your specific question is. Are you asking why we need to structure programs? – Jörg W Mittag Feb 28 '16 at 14:50
  • 1
    @JörgWMittag: I'm NOT asking why we need to structure programs. Obviously a large problem is split in smaller ones that you can solve and then combine to get the solution for the large one. I'm asking what problems Monads solve. Is that it? Structuring code? Is that all the fuss is about? For example, in Haskell this is how you do I/O. There, the monad is a play pretend acting like something pure when in fact is isn't. My question is in the title, I don't know how to state that in a clearer way. – Dummy Me Feb 28 '16 at 14:58
  • 3
    [Why do we need monads?](http://stackoverflow.com/questions/28139259/why-do-we-need-monads) – Robert Harvey Feb 28 '16 at 15:45
  • 2
    [Why monads?](http://stackoverflow.com/q/7840126) – Robert Harvey Feb 28 '16 at 15:53
  • ... and [this post](http://marijnhaverbeke.nl/monad.html), which offers this assertion: *"They are the wonderful abstraction that allows Haskell to be a pure functional language without being completely unusable. "* – Robert Harvey Feb 28 '16 at 16:07
  • 1
    @RobertHarvey: I also found the first two links you added but like in the answers of those and other posts, the responses go straight to Maybe, State and IO monads, quickly jumping to examples or code, and end with "there is a lot of problems that can be solved using Monads". Which are the other kinds of problems? I'm looking for a higher level answer that instead of reiterating the same examples actually goes to the root of the problems (whatever those are) and explain how Monads solve them and why it's the best choice, as opposed to using something else. Thanks a lot for the feedback though. – Dummy Me Feb 28 '16 at 18:08
  • 1
    Do you understand the essential concept? Side-effects and mutation on one side of the monad, pure functions and immutability on the other. You might also be interested in [monads and gonads](https://www.youtube.com/watch?v=dkZFtimgAcM), in which Douglas Crockford not only explains the monad in plain language from a Javascript perspective, but how the monad has been in Javascript programming all along (in the form of AJAX functions). – Robert Harvey Feb 28 '16 at 18:17
  • "For example, in Haskell this is how you do I/O. There, the monad is a play pretend acting like something pure when in fact is isn't." - this is untrue. The IO monad actually allows you to write pure functions that interact with the environment. The "main" function is a pure function that returns a description of a sequence of actions to take, combined with a list of pure functions to call that will receive the results of those actions. The code that receives the monad returned by main is impure, but that is part of the environment, not part of a user program. – Jules Feb 28 '16 at 19:15
  • Recommended reading: [Tackling the Awkward Squad](http://research.microsoft.com/en-us/um/people/simonpj/papers/marktoberdorf/mark.pdf) by [Simon Peyton Jones](https://en.wikipedia.org/wiki/Simon_Peyton_Jones) (one of the designers of Haskell). It explains the motivation for introducing monadic IO to Haskell, which is one of many uses of monads. Very interesting read! – Andres F. Feb 28 '16 at 23:13
  • 1
    @DummyMe: The point of the IO monad is not to "pretend like something is pure when in fact it isn't". Rather it is the other way around - to make IO effects explicit in the type system. This makes it apparent which part of the program is pure and which is not. – JacquesB Feb 29 '16 at 07:54
  • The point of Monads is that you can have generic functions that do different things depended on the type of your context, and that you can represent in the type system what type of semantics you use in your computations. – aoeu256 Mar 25 '21 at 15:12

4 Answers4

10

You don't need monads to solve anything. They just make certain things simpler. A lot of people go way too abstract and theoretical when explaining monads. Mostly, monads are a pattern that comes up over and over again in programming. By recognizing that pattern, we can simplify our code and avoid reimplementing certain functions.

For Haskell, from a concrete point of view, the most visible thing monads enable is do notation. List comprehensions in Haskell and other languages also take heavy advantage of monads. You also can create libraries like Control.Monad.

These all provide useful simplifications, and once you have implemented it for one monad, you automatically get it for all monads. This is one of the major reasons why code reuse is so much easier for functional programming than other paradigms.

Karl Bielefeldt
  • 146,727
  • 38
  • 279
  • 479
  • 2
    I'd actually say that the most visible thing haskell monads enable is an easy way to have IO effects that actually work the way you expect. I think anyone wondering what monads give us should try out [Miranda](http://miranda.org.uk/). Miranda is the language Haskell was designed to replace, and should be very easy to learn for anyone with Haskell experience. The main difference is that it doesn't have monadic IO, which makes the language much harder to work with than Haskell for any non-trivial project. – Jules Feb 28 '16 at 19:46
  • Yes, `IO` is Haskell's most prominent *monad*, but I wasn't listing examples of individual monads. I was listing examples of the overarching abstractions they enable. I was trying to get at why, for example, the first guy to think of using a monad for IO would think it's a good idea in general. – Karl Bielefeldt Feb 28 '16 at 22:30
  • 1
    @Jules: Or look at the history of Haskell. Before Monads, the Haskell designers experimented with Lazy Streams and Continuations as the basis for I/O, and both were hard to use. – Jörg W Mittag Feb 28 '16 at 23:41
5

It is easier to understand if you look at the particular monads and see what problems they solve. For example, in Haskell:

  • IO: Allows IO to be represented in the type system, so you can cleary seperate pure functions from functions performing IO.

  • List: Allows you to do list comprehensions and nodeterministic computations.

  • Maybe: A better alternative to nulls, and supporting something comparable to C#'s null-coalescing operator.

  • Parsec: A convenient DSL for writing parsers.

So it is easy (I hope) to see the rationale for the individual monads, since they are all pretty useful. But the problems they solve are also quite different and on the face of it they don't seem to have much in common, except they are all related to some logic for chaining operations. Monads are useful because they allow you to build such a variety of tools.

Could the above examples be implemented without monads? Certainly they could be implemented in an ad-hoc manner, but having monads built into the language allows direct langauge support like the do-notation which works with all monads.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
2

One thing that makes it confusing is that the "popular" functions like bind and <*> are praxis oriented. But to understand the concepts it's easier to look at other functions first. It's also worth noting that monads stand out because they are a bit overhyped in comparison to other connected concepts. So I will start with functors instead.

Functors offer a function (in Haskell notation) fmap :: (Functor f) => (a -> b) -> f a -> f b. In other words you have a context f that you can lift a function into. As you can imagine almost anything is a functor. Lists, Maybe, Either, functions, I/O, tuples, parsers... Each represents a context in which a value can appear. So you can write extremely versatile functions that work in almost any context by using fmap or its inline variant <$>.

What other stuff do you want to do with contexts? You might want to combine two contexts. So you might want to get a generalization of zip :: [a] -> [b] -> [(a,b)] for example like this: pair :: (Monoidal f) => f a -> f b -> f (a,b).

But because it's even more useful in practice, the Haskell libraries instead offer Applicative, which is a combination of Functor and Monoidal, And also of Unit, which just adds that you can actually put values "inside" your context with unit.

You can write extremely generic functions by just stating these three things about the context you are working in.

Monad is just another thing you can state on top of that. What I didn't mention before is that you already have two ways to combine two contexts: You can not only pair them, but you can also stack them, e.g. you can have a list of lists. In the I/O context, an example would be an I/O action that can read other I/O actions from a file, so you would have a type FilePath -> IO (IO a). How can we get rid of that stacking to get an executable function IO a? That's where Monads join comes in, it allows us to combine two stacked contexts of the same type. The same goes for parsers, Maybe etc. And bind is just a more practical way to use join

So a monadic context only has to offer four things and it can be used with almost all the machinery developed for I/O, for parsers, for failures etc.

MarLinn
  • 161
  • 4
1

Monads lets you express various non-pure computations as well as simplifying code

  • stateful computation (get/set state via monad)
  • I/O (logging, UI, file or just produce/consume a list of X)
  • Also, "non-linear" control-flow (i.e. exceptions, maybe, etc.)

And, importantly without compromising with the pure language constructs and getting a cleaner language out of it

Macke
  • 2,576
  • 16
  • 16