Hacker News new | ask | show | jobs
by Muphrid 3926 days ago
Whoever told you it has to do with IO and functional purity vastly oversimplified things. That is just one use case.

Monads take care of a very common computation pattern: wrapping and unwrapping data from a container to do stuff with it. This process is error-prone and leads to code bloat if done by hand whenever needed.

I mean, how often have you had to apply a function to every element in a vector of values? fmap takes care of that without having to go into the list, take a value one at a time, and apply the function you want to use.

Rather than have to unwrap your container to use functions, a monad offers the means to transform functions to use a monadic container as input instead. No wrapping or unwrapping by hand required; using bind, fmap, or ap(ply), you transform lots of functions to use the monadic container and build a seamless pipeline for data to traverse.

How often have you had to deal with a value that might or might not exist--e.g. searching a string for a substring's position? If you had that problem, you might say, well, I'm going to return an integer, but a negative number means no match. And then in all code thereafter, I have to check if the integer is negative and do different stuff. A simple Maybe/Optional monad would take care of all that for you.

How often have you had to write verbose output alongside a computation? You could pepper your code with print statements, but maybe you want that output controlled by some runtime parameter. Would you want to check at every print statement whether that parameter is true? Or you could use a Writer monad, pass along the verbose output through the computation, with an easy means to transform regular functions into using Writers, and then decide what to do with the output at the end.

Sure, it's not convenient to try to use monads if the language doesn't offer it, or if there's not a library to facilitate it (trust me, I know this; I implemented a slew of templates to use monads in C++).

But programmers do this stuff all the time: unwrap data, do stuff with it, pack it back up. They do this over and over, repeating themselves, and such repetitive code is a maintenance trap waiting to happen.

Monads help you avoid that trap, and they help you take your program and reduce it to "one thing in, one thing out." Now maybe those "one things" are containers, but linearizing the flow of data this way only helps make the program's concept easier to understand.

1 comments

> Monads take care of a very common computation pattern: wrapping and unwrapping data from a container to do stuff with it.

Isn't this what Functor does with `fmap`, not Monad?

Even if Monad did not exist and you only had Functor and Applicative, you'd be able to compose `pure` and `fmap` to get the same effect as bind, right?

No, plain functors, applicative functors, and monads each deal with a different potential use case.

Look at the specific type signatures:

  plain functor - fmap: (a -> b) -> (F a -> F b)
  applicative functor - ap: A (a -> b) -> (A a -> A b)
  monad - bind: (a -> M b) -> (M a -> M b)
So yes, monads are not unique in doing wrapping and unwrapping data to use with functions--all functors do this--but all monads are also functors, and also applicative functors, so they do everything the other two do, and more. You can't get at anything like bind using just fmap, ap, and pure/unit. But you can write fmap and ap merely in terms of bind and unit.