Hacker News new | ask | show | jobs
by Izkata 2123 days ago
> Anything that defines that sort of function application in a particularly convenient way is a monad. There's more to it, but that's why it's useful.

Aaand with this statement you skipped over what IMO is the most important missing piece, because everything above it fits higher-order functions such as map(), which as far as I understand aren't monads.

1 comments

I disagree. Map (or a functor) alone can't do everything I've said. There are a few reasons. I assume you're already familiar with the subject matter, so I'm going to cross my fingers that you know it in haskell and use that notation and terminology to save us time.

tl;dr map works for applying the simplest functions to "containers." To apply more interesting functions, you need bind, pure/return, and/or whatever applicative's <*> is called.

Let's say you're working in something like Maybe. You might want to write some code like

    f :: Float -> Float
    f x = 5 + x
In that case map is fine. fmap lets you focus on simple code like that. Or maybe you want to write

    f :: Float -> Maybe Float
    f x = if x == 0
          then None
          else 5 / x
In that case you need more than map, because you don't want to deal with what map would give you: a Maybe (Maybe Float). (>>=) lets you still focus on simple code like this, since you dont have to deal with any unpacking/flattening, which was what I'm saying is why monads are so useful.

More importantly, you need more of the FAM hierarchy than map if you care about multivariate functions, which I'd say is most code. Lets say we're working with

    f :: Float -> Float -> Float
    f x y = x + y
We want to write code like that and use some version of function application (like map). If we just use map, we get the following

    f <$> maybeX :: Maybe (Float -> Float)
which isn't at all what we want, because we can't apply it to a maybeY (or even to a float y, which `pure`/`return` lets us treat as a maybeY). If we define an additional way to apply that Maybe (Float -> Float) to a Maybe Float, we've defined Applicative, forcing us to go beyond a functor.

The motivation and usefulness is the same throughout: we just want to write code and apply functions that don't care about the structures emitting/containing our inputs and outputs. It just turns out that there are three cases depending on the kinds of functions we're writing and applying

   f :: a -> b  -- functor is sufficient, like you say.
   f :: a -> b -> c -- functor isn't sufficient. applicative is.
   f :: a -> m b -- functor and applicative aren't sufficient. monad is.
I wrote a series of posts ages ago deriving all these from that one motivation (in a more fleshed out manner) http://imh.github.io/2016/05/26/why-monads.html
> I assume you're already familiar with the subject matter

Nope. I think this is why you don't see where the previous post falls short, too much familiarity so you don't realize you're skipping over important aspects.

> so I'm going to cross my fingers that you know it in haskell and use that notation and terminology to save us time.

I get just enough Haskell to understand the first 3 code blocks, and can guess what the 4th is depicting, but am not sure.

Sorry, I thought when you said missing piece in your previous message, that you meant the missing piece of what makes monads unique (arguing from a place of knowledge about “no THIS is what makes monads important”), rather than the missing piece of the explanation. Either way, hopefully the posts I wrote and linked are written assuming nothing more than the basic Haskell syntax, so if you’re curious about the rest of the explanation, hopefully it’s clearer there. If it’s not clear there, I’d appreciate any feedback.