3 ms·
I agree, generally; it's a bit hard to write something better than RWH over a lunch break. But sometimes, breaking down the same problem from multiple viewpoint
by jmillikin 17y ago
I agree, generally; it's a bit hard to write something better than RWH over a lunch break. But sometimes, breaking down the same problem from multiple viewpoints helps readers to understand what's going on.
My particular purpose in writing this is to demystify monads specifically, especially for people who are casually interested in learning Haskell but unwilling to read a few hundred pages on it. RWH is great, but it doesn't explain monads until chapter 14 -- that's a lot of material a reader has to understand before they can even parse the examples.
- jrockway 17y agoBut sometimes, breaking down the same problem from multiple viewpoints helps readers to understand what's going on. I think 43% of the confusion about monads is due to the sheer number of "monad tutorials", each explaining two or three functions (return/bind, pure/fmap/join) with different, overly-complex analogies. (A monad is like a burrito, a monad is like a space suit, a monad is like a nuclear waste container...) The tutorials start with some analogy, and then inevitably explain how "the list monad" and "the maybe monad" work. At the end you know that monads are like burritos, how to apply a function to every value in a list, and how to return failure from a function. But you still don't know anything about monads, or more importantly why monads. If I were to write a monad tutorial, it would include these key points: 1) A monad is not a thing, it's a property of other things. Maybe is a thing. A list is a thing. Both are monads. (My bike is a thing. My wall is a thing. Both are blue.) 2) The abstract concept of a monad lets you reuse code. If you want to apply a function to every value of a list, you could do that without monads. If you want functions to be able to fail, and you want the failures to be composable, you could do that without monads. But what you can't do without monads is write a function like liftM2. With monads, you can turn a normal function into one that works on lists. Or that works on IO. Or that works on computations that can fail. You get to use the same function for all three types. That's the point of monads. (Then there will be a code example: 2 + 3 --> 5 liftM2 (+) (Just 2) (Just 3) --> Just 5 liftM2 (+) Nothing (Just 3) --> Nothing liftM2 (+) (Just 2) Nothing --> Nothing liftM2 (+) [1,2] [3,4] --> [4,5,5,6] liftM2 (+) readLn readLn --> 1 <ENT> 2 <ENT> --> 3 Then it all makes sense!)
- jmillikin 17y ago(note: HN mangles <*>, so I'm using <∗> instead Isn't #2 a property of functors in general? I know I usually use <$> and <∗> rather than the liftM family because they don't limit the number of parameters. (+) <$> (Just 2) <∗> (Just 3) (+) <$> [1,2] <∗> [3,4] (+) <$> readLn <∗> readLn
- jrockway 17y agoYes.
- ch 17y agoDoes this mean that you could go on an say: (+) <$> (Just 2) <∗> (Just 3) <∗> (Just 5) And it would have the same meaning as: liftM3 (+) (Just 2) (Just 3) (Just 5) edited for formatting
- jmillikin 17y agoExactly. You can use the applicative operators on functions with an arbitrary number of parameters. Note, though, that (+) only takes two parameters, so your examples won't work as written. These should be equivalent: enumFromThenTo <$> Just 2 <*> Just 3 <*> Just 5 liftM3 enumFromThenTo (Just 2) (Just 3) (Just 5)
- ch 17y agoNeat. I did see that liftM3 takes a function of 3 arguments, and just plain ignored that fact in my example :) And to be clear, for <$> and <*>, you need a Functor instance defined for you type right? Are there benefits to using one or the other, they seem to evaluate the same, is GHC able to optimize Monad and Functor equally?
- jmillikin 17y agoRight -- liftM and friends are for monads, <$> and <∗> are for functors. All monads are supposed to be functors, though there's currently no type-level guarantee for historical reasons. Most instances of Monad also provide an instance of Functor. Are there benefits to using one or the other, they seem to evaluate the same, is GHC able to optimize Monad and Functor equally? I believe there's currently no special optimizations for either case, though as I don't poke around in GHC this statement may be mistaken. The primary advantage of the application operators over liftM is that there's no liftM6, liftM7, etc; you can type <∗> as much as you want.