Live data from Hacker News

Functors, Applicatives, and Monads in Pictures (2013)

adit.io

51–56 of 56 posts

Re: Functors, Applicatives, and Monads in Pictures (2013)

#51
post #14
post #2

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…

[deleted]

Re: Functors, Applicatives, and Monads in Pictures (2013)

#52
post #2

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…

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?

Re: Functors, Applicatives, and Monads in Pictures (2013)

#53

Earlier 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 don't write O(N) class-specific implementations. You write only one function implementation.

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)

#54
post #50
post #14

Earlier 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?

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.

Re: Functors, Applicatives, and Monads in Pictures (2013)

#55
post #46

Earlier 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.

That operation is called fmap, and it's characteristic of a functor. In Haskell, it's called liftM for monads, but they're really the same thing (and I think recent proposals are going to iron out this redundancy). fmap is one of the properties of a functor. All monads are functors, so all monads provide an operation with that signature.

Re: Functors, Applicatives, and Monads in Pictures (2013)

#56
post #52

Earlier 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?

Control structures, generally.

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.
Post reply on HN