Live data from Hacker News

Functors, Applicatives, and Monads in Pictures (2013)

adit.io

11–20 of 56 posts

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

#11
post #6
post #3

Earlier quoted context omitted.

The reason that the Monad structure is interesting, in my view, is that it's simple and neatly captures the notion of a "computation" that we can compose in different ways. The core useful operator for monads in Haskell is >>=. It has the following type: m a -> (a -> m b) -> m b If you squint, this is like function application with a few extra m's thrown in. Here's normal function application for comparison: a -> (a…

I appreciate your explanation but again it fails to actually show it being useful outside of the context of working around Haskell's strict type system. What the OP and myself would like to see is concrete examples why this is a better approach than the way something would be done without explicitly caring about monads (I say explicitly because I know there is a tendency to say that some given structure is a monad an…

You know how ES6 has this big hoorah coming because they're finally getting something to make callback hell a little less hellish, async/await?

Well. It's a monad. It's about 30s of work to implement it if you know what you're doing.

Of course, you could implement it as a monad in Javascript as well. It'd be a neat trick, but you'd never love it. The reason is simple: you can't benefit from monads unless you use them so pervasively that everything will integrate together.

And if you do, that integrate together bit works fantastically. People often complain about monads not composing---but I think they're often just misinterpreting a rather technical result. Actually, imo, monads compose incredibly well and it's astounding when you get used to it.

When you use them pervasively, monads mean that you get to "pick your own semantics" on the fly, whenever you want. You can mix and match semantics as is interesting and work with your custom mixes as if they were built into the language.

So people, e.g., talk about how it was easy to write STM because of monads.

It wasn't "because of monads". Of course STM is a monad and anything which looks even halfway like it in any language will also be a monad no matter how hard you try to avoid it. They're "just a pattern".

But when you've got a language which allows you to cut into the "STM monad" exactly and whenever you want, when you've got a hold of the root of the semantics of your language, when you've got programmable semicolons, then there's something really special.

And without that you've got a couple of years of bickering between standards committees to fix what end up being trivial looking problem.s

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

#12
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…

Have you used jQuery? The way that selectors and method chaining works has a monadic flavor which is very convenient. In the alternate reality where haskell is running in your browser, if you were creating jQuery from scratch you would recognize that the thing you were making was monadic, you would make use of the large number of useful functions that fall out from Monad here: http://hackage.haskell.org/package/base-4.8.1.0/docs/Control..., and you would also recognize that much of what you needed in your jQuery API is already provided by those functions, so no need to re-implement things in an ad hoc way and force your users to learn new useless things. You might even find that what you're trying to create is a mashup, or "stack", of two or three different monadic things that have already been defined. You'd know that because your new jQuery structure obeys a couple simple rules that it is well-behaved and reasonable in the presence of those functions (and others that don't exist yet), and you may even notice some optimizations you can confidently make.

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

#13
post #6
post #3

Earlier quoted context omitted.

The reason that the Monad structure is interesting, in my view, is that it's simple and neatly captures the notion of a "computation" that we can compose in different ways. The core useful operator for monads in Haskell is >>=. It has the following type: m a -> (a -> m b) -> m b If you squint, this is like function application with a few extra m's thrown in. Here's normal function application for comparison: a -> (a…

I appreciate your explanation but again it fails to actually show it being useful outside of the context of working around Haskell's strict type system. What the OP and myself would like to see is concrete examples why this is a better approach than the way something would be done without explicitly caring about monads (I say explicitly because I know there is a tendency to say that some given structure is a monad an…

I'll take a shot at it.

Quoting tikhoni:

> For Maybe, being a monad gives us a standard way of working with values while automatically dealing with Nothing. It abstracts over repetitive null checking and lets us easily build up Maybe values based on other Maybe values.

To expand on that a bit, in a way that might work for you (or might not): I've got a function f a that exists. It works. It does exactly what I want. Unfortunately, the data I've got is a Maybe a, not just an a. So I can't use my nifty function f, because it takes an a. You can feel my annoyance/frustration - so near, and yet so far.

But, in fact, I can use my function f, because Maybe is a monad. That lets me apply f to my Maybe a, without having to change either f or a whatsoever. I don't even have to write the magic code to do this - Maybe already has it.

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

#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 takes care of that without having to go into the list, take a value one at a time, and apply the function you want to use.

Rather than have to unwrap your container to use functions, a monad offers the means to transform functions to use a monadic container as input instead. No wrapping or unwrapping by hand required; using bind, fmap, or ap(ply), you transform lots of functions to use the monadic container and build a seamless pipeline for data to traverse.

How often have you had to deal with a value that might or might not exist--e.g. searching a string for a substring's position? If you had that problem, you might say, well, I'm going to return an integer, but a negative number means no match. And then in all code thereafter, I have to check if the integer is negative and do different stuff. A simple Maybe/Optional monad would take care of all that for you.

How often have you had to write verbose output alongside a computation? You could pepper your code with print statements, but maybe you want that output controlled by some runtime parameter. Would you want to check at every print statement whether that parameter is true? Or you could use a Writer monad, pass along the verbose output through the computation, with an easy means to transform regular functions into using Writers, and then decide what to do with the output at the end.

Sure, it's not convenient to try to use monads if the language doesn't offer it, or if there's not a library to facilitate it (trust me, I know this; I implemented a slew of templates to use monads in C++).

But programmers do this stuff all the time: unwrap data, do stuff with it, pack it back up. They do this over and over, repeating themselves, and such repetitive code is a maintenance trap waiting to happen.

Monads help you avoid that trap, and they help you take your program and reduce it to "one thing in, one thing out." Now maybe those "one things" are containers, but linearizing the flow of data this way only helps make the program's concept easier to understand.

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

#15
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…

I tried covering it (and showing there's a better way) in this talk: https://www.youtube.com/watch?v=449j7oKQVkc

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

#16
post #6
post #3

Earlier quoted context omitted.

The reason that the Monad structure is interesting, in my view, is that it's simple and neatly captures the notion of a "computation" that we can compose in different ways. The core useful operator for monads in Haskell is >>=. It has the following type: m a -> (a -> m b) -> m b If you squint, this is like function application with a few extra m's thrown in. Here's normal function application for comparison: a -> (a…

I appreciate your explanation but again it fails to actually show it being useful outside of the context of working around Haskell's strict type system. What the OP and myself would like to see is concrete examples why this is a better approach than the way something would be done without explicitly caring about monads (I say explicitly because I know there is a tendency to say that some given structure is a monad an…

> I say explicitly because I know there is a tendency to say that some given structure is a monad and we didn't realise it; but that still doesn't really prove that it is all that useful in the general sense

Exactly. That is what I call mathematical- vs. cognitive-abstraction. They are not the same thing, and in the case of programming languages, only the latter matters. A mathematical abstraction is simply the naming of a pattern; a cognitive abstraction is a human solving a problem by explicitly thinking of said pattern. The former is either true or false; the latter is either useful or not (and the utility is completely orthogonal to any mathematical considerations).

> I appreciate your explanation but again it fails to actually show it being useful outside of the context of working around Haskell's strict type system

I think it isn't useful as a cognitive abstraction (and the problem isn't Haskell's type system, but its purity from effects). I believe that monads as a cognitive abstraction are foreign and harmful to imperative languages, which have an equally-powerful cognitive abstraction -- the thread -- that fits much better with the rest of the language's abstractions and I think is much more useful for most people.

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

#17
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…

Railway oriented programming; Error handling in functional languages

https://www.youtube.com/watch?v=UvD1VjRvGIk

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

#18
Off-topic, but why are Haskell's function signatures written like this?

    function_name :: Param1Type -> Param2Type -> Param3Type -> ReturnType
To me that just makes no sense. There's no way to logically "read" it. The natural way would be "function_name converts a Param1Type into a Param2Type and then ... what?! Is this a chain of functions?"

Why not have something sensible like this?

    function_name :: Param1Type, Param2Type, Param3Type -> ReturnType

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

#19

Off-topic, but why are Haskell's function signatures written like this? function_name :: Param1Type -> Param2Type -> Param3Type -> ReturnType To me that just makes no sense. There's no way to logically "read" it. The natural way would be "function_name converts a Param1Type into a Param2Type and then ... what?! Is this a chain of functions?" Why not have something sensible like this? function_name :: Param1Type, Para…

because in haskell functions are curried,

  function_name :: param1 -> param2 -> return
is a function that if you only give it param1 it returns a function that takes an argument of param2Type and returns returnType. This way you can easily compose functions

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

#20

Off-topic, but why are Haskell's function signatures written like this? function_name :: Param1Type -> Param2Type -> Param3Type -> ReturnType To me that just makes no sense. There's no way to logically "read" it. The natural way would be "function_name converts a Param1Type into a Param2Type and then ... what?! Is this a chain of functions?" Why not have something sensible like this? function_name :: Param1Type, Para…

Because you write functions like this in mathematics as well.

What we usually see is something like this:

    f(x) = y
But if you look at it carefully the function f is a mapping of the domain x to the range y (the arrow is f), written like so:

    x -> y
Of course, the domain, being a set of all permissible values, is the type. So we write the type instead:

    type1 -> type2
Everything in Haskell is a function, and pure functions only ever take ONE parameter. Therefore the commas make no sense. The name in front just aids in naming stuff.

If you have a function that takes 2 parameters, they'd have to be curried. How would you represent curried functions?

    f::type1 -> type2 -> type3 
To make it more concrete, let's look at add instead of f.

So we can define a function like this (let's use Python for its readability):

    def add(a, b): return a + b
But remember, in haskell, functions are pure. Meaning they only map one input to one output. In order to make this happen, we need to split the function up into parts that only take one parameter.

Let's start with the plus operator as a function (it is one in Haskell just made into an infix). To think about it, it'd be something like this:

    plus(a)(b)
Where plus(a) is defined as:

    def plus(a): return plusA
Hence the first part would become a curried function like so, which takes another parameter:

    plusA(b)
Where plusA() is defined as such:

    def plusA(b): return b + a # a is a constant

So if you look at it from the types it was being transformed from:

    plus() # takes a Real, returns plusA.

    plusA(___) # takes a Real, returns a Real. written as (Real -> Real)
So if you put it together, the function signature for plus() is:

plus() takes Real -

    plus :: Real->
plus() returns plusA (which is Real->Real) -

    plus :: Real-> (Real-> Real)

Or in other words we can write it as such

    add :: Real -> Real -> Real
Post reply on HN