33 ms·
That's a pretty good explanation, thanks. Along with the monads in Clojure [0] article I read recently, I feel I'm getting a decent grasp on monads finally. No
by widdershins 7y ago
That's a pretty good explanation, thanks. Along with the monads in Clojure [0] article I read recently, I feel I'm getting a decent grasp on monads finally.
Now they're beginning to click, I don't feel they're too complicated. Simply a way to insert a converter function between each stage in a good-old-fashioned pipeline of function applications? The converter takes the output of each function, transforms it so that it can be piped into the next function, then takes that output and combines it with the previous one, etc. Please, someone enlighten me if I've got the wrong end of the stick.
[0] https://github.com/khinsen/monads-in-clojure/blob/master/PART1.md https://github.com/khinsen/monads-in-clojure/blob/master/PAR...
- rusticpenn 7y agoYou are right. Its just a simple converter function to use in a pipeline.
- IceDane 7y agoNo, not even close.
- mikekchar 7y agoYou have it right. It is literally a container that allows you to pipeline functions on its contents. The only technical catch is that you aren't allowed to change the type of the contents in the function. That means if you start with something of type A in the container, you have to get a container containing something of type A at each stage of the pipeline. There are probably 3 popular uses for monads. First to defer error handling. You allow type A to include some kind of error state. The you run your entire pipeline of functions, but at every stage if the container contains an error, you just return a container with the same error as output. That way, none of your pipeline stages need to check for valid input -- they just assume there were no errors before their stage. The second main case is to defer processing until a later time. You have a pipeline of functions and a container. As long as you don't look at the result of running that pipeline, even if the container doesn't contain anything yet you can pretend that it represents your result. You can pass that along in your program, gradually appending more functions on your pipeline as you go. When you finally put something in the container, you can see what the result is. This is useful when you don't know what your result is right now, but you know what processing you want to do when you get the input (for example, if you are going to query some external system and when it returns you want to do some processing. You can go ahead and pretend to process that result and when the data finally arrives, then you can actually get the result of the computation). The third main case is when you have a big list and you want to narrow it. For instance, you might have a list of food. You want to narrow that to a list of fruit. Then you want to narrow that to a list of red fruit. Then you want to narrow that to a list of red fruits that are berries. Instead of writing a function to do your whole query, you can separate it: A query for fruit. A query for red. A query for berries. This can make your code a lot easier to read. There are other things you can do with monads, but that's the basics IMHO. They really are simple, but suprisingly enlightening. You might think, "This constraint that I return the same type each time is kind of annoying", but if you spend a fair amount of time with monads you will see that this allows you to make pretty amazing assumptions in your code -- which, in turn, let you do very powerful things.
- mrkeen 7y ago> The only technical catch is that you aren't allowed to change the type of the contents in the function Sticking with your metaphor of container + contents, the contents absolutely can change. It's the container that has to remain the same throughout the pipeline.
- Patient0 7y ago" The only technical catch is that you aren't allowed to change the type of the contents in the function. That means if you start with something of type A in the container, you have to get a container containing something of type A at each stage of the pipeline." I don't think this is correct. What doesn't change is the overall type constructor (e.g. if you start with a list, you'll still end up with a list... If you start with an IO action, you'll still have an IO action). But the type that is wrapped by the container can and usually does change. e.g. if you "map" over a list of A's, you'll get a list of whatever type your map function returns - still a list, but a list of B's, not a list of A's. fmap :: (a->b) -> [a] -> [b].
- mikekchar 7y agoReplying to my own post because I'm too late to edit and there are two replies that rightly point out my mistake :-) Thanks!
- chongli 7y agoSimply a way to insert a converter function between each stage in a good-old-fashioned pipeline Yes, but more general. Some monad tutorials describe them as "overloading the semicolon." I felt I didn't really understand them until I got the idea of computations as values. It's much harder to motivate monads in a dynamic language. They were invented to solve problems in Haskell and it is there where they truly shine. Having said that, most of their thunder has been stolen by applicative functors (which generalize monads) these days.
- lmm 7y agoIt doesn't necessarily convert. You can compose with a function and the types will line up the way you'd expect, but you can't assume that the function is always being fed a single value - the function might be called zero, one, or many times, or stored to be called later. It might even dynamically be called either now or later. I find the most common misconception is to assume that all monads are "container-like", somehow having a value "inside" them. Whereas actually it's much more general than that. I'd recommend playing around with some non-container monads - e.g. future or continuation. Personally I found it was easier to grasp the general case once I had experience with a bunch of specific examples.
- dmix 7y agoIt’s also helpful to eventually learn Haskell and learn why they needed something like Monads to cleanly solve various problems given the safety and purity constraints imposed by the language. http://cs.coloradocollege.edu/~bylvisaker/MonadMotivation/ http://cs.coloradocollege.edu/~bylvisaker/MonadMotivation/ Although that can quickly turn into an academic rabbit hole of theory so it helps to keep it connected to your real world usage instead of getting fancy. Which is why I found it helpful to learn in the context of actually using Haskell and playing with real code.