Nice pictures, but I just don't get it. Sure, it's very easy for me to understand monads mathematically, what kind of structure are they. I don't need any pictures for that, the definition suffices for me. But that doesn't tell me what are they good for . We invent things for a reason, and I don't see the reason. Now I know the reason, it's been told to me many times (some way to wrap I/O while preserving functional…
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…
Functors, Applicatives, and Monads in Pictures (2013)
51–56 of 56 posts
Re: Functors, Applicatives, and Monads in Pictures (2013)
#52Nice pictures, but I just don't get it. Sure, it's very easy for me to understand monads mathematically, what kind of structure are they. I don't need any pictures for that, the definition suffices for me. But that doesn't tell me what are they good for . We invent things for a reason, and I don't see the reason. Now I know the reason, it's been told to me many times (some way to wrap I/O while preserving functional…
You kind of just have to use them to “get it”, but here’s an analogy: if you write a data structure, you naturally want to make it “iterable” in your language’s usual way, so you can take advantage of a library of generic functions for working with iterable things. Same goes for monads. If we have N data types and M functions, instead of writing N×M implementations, we can write just N monad instances + M generic imp…
Re: Functors, Applicatives, and Monads in Pictures (2013)
#53Earlier quoted context omitted.
> Same goes for monads. If we have N data types and M functions, instead of writing N×M implementations, we can write just N monad instances + M generic implementations. Bear with me for a bit, because I still don't get it (although I'm not the OP, I share the same doubts). In OO terms, if you have N types and M functions, with M different behaviours (function code), you aggregate the N types into an inheritance tree…
> In OO terms, if you have N types and M functions, with M different behaviours (function code), you aggregate the N types into an inheritance tree that makes you write 1xM functions (one function against the ancestor of the N types). In general, its O(M+N), because you write the M methods in the ancestor that you talk about, and then O(N) class-specific implementations of the underlying functionality on which the M…
You only have to write N class specific implementations if each class shows a different behaviour and thus requires a different implementation. If one function code can handle N different classes that share 1 behaviour, you encode this behavioural information in the class hierarchies: by inheritance/delegation, you inform the type system that the N classes share a common behaviour. You write the code in one class, and have the N classes either inherit from that class or delegate onto that class (inheritance vs delegation is orthogonal to this discussion).
Re: Functors, Applicatives, and Monads in Pictures (2013)
#54Earlier quoted context omitted.
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…
> 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?
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.Re: Functors, Applicatives, and Monads in Pictures (2013)
#55Earlier quoted context omitted.
Isn't Maybe being an instance of Applicative enough in this case?
Maybe... But seriously, I guess I think it's not a Monad. The Monad signature (in this case) is (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b and I think what we really want (and what I said we wanted to do) is Maybe a -> (a -> b) -> Maybe b I don't know whether that's Applicative or Functor, but I don't think it's Monad.
Re: Functors, Applicatives, and Monads in Pictures (2013)
#56Earlier quoted context omitted.
You kind of just have to use them to “get it”, but here’s an analogy: if you write a data structure, you naturally want to make it “iterable” in your language’s usual way, so you can take advantage of a library of generic functions for working with iterable things. Same goes for monads. If we have N data types and M functions, instead of writing N×M implementations, we can write just N monad instances + M generic imp…
So what are the M generic functions that we can apply to all monads?
For example, consider a pattern for reading length-prefixed lists: read a number N, then read N numbers, then sum them.
main :: IO ()
main = do
n
“replicateM” (replicate + M for monadic) does an action N times and accumulates the results. Above we used it in IO, but we can use it in any monad, for example a parser: import Text.Parsec
import Text.Parsec.String
import Control.Monad
main :: IO ()
main = do
line (read many1 digit)
And if we wanted to abstract over this pattern, we could do so trivially: lengthPrefixedSum :: (Monad m) => m Int -> m Int
lengthPrefixedSum get = do
n
Now our first “main” becomes: main = print =
And “sumParser” becomes: sumParser = lengthPrefixedSum number
There are many such functions in the standard library, such as “forM” which implements “for each” loops, and “when” which implements conditionals. If your data type is a monad, all of these control structures are available to you for free, so you can easily implement very expressive DSLs.