Live data from Hacker News

Functors, Applicatives, and Monads

thecoder.cafe

11–20 of 90 posts

Re: Functors, Applicatives, and Monads

#11

the bit at the end is quite rude of the haskeller responding but I also think they're largely right; another monads explained through boxes tutorial is not gonna help anyone. In fact it's really a step in the wrong direction. Using a few different monads is where to start.

Was it rudeness or honesty without malice? The "monad tutorial" instinct is a well-documented fallacy. In my culture we don't whitewash our opinions to make them palatable to someone who's obviously doing something wrong in a known way.

Re: Functors, Applicatives, and Monads

#12
post #6

For some reason everyone likes to talk about Monads, but really the other types here are just as interesting. For example, Applicatives are less dynamic than Monads in that you can't `flatMap`/`bind` to decide on the "next" thing to evaluate based on the previous value, but in exchange you get a "static" tree (or graph) of Applicatives that lends itself much better to static analysis, optimization, parallelism, and s…

> Monads seem to have this strange aura around them that attracts certain kinds of personalities

Historical accident.

There was a time, not very long ago, when we didn't know applicative functors were a useful separate subset of monads. We thought full monads were needed for all the neat things that applicatives are sufficient for.

During this time, lots of ink was spilled over monads. Had we invented applicative functors a little earlier, they would probably have gotten more of the spotlight they deserve.

-----

I also think people underappreciate the humble semigroup/monoid. But this is not historical accident, it is just that it seems to simple to be useful. But it is useful to be able to write functions generic over concatenation!

Re: Functors, Applicatives, and Monads

#13
I feel like Haskell is easier to use than it is to explain, and in my experience a lot of these kind of tutorial / explanations actually make things seem harder and more complicated than just working with the concepts and observing what they do. (This one included.)

Re: Functors, Applicatives, and Monads

#14
I think it's great that people are excited about Haskell and want to write about it, and it's unfortunate that the author had to deal with a less thank tactful response to their work. I hope the author keeps spending time with Haskell and continues to make time to try to write more and help other people!

That said, here is a bit of a long comment on my thoughts about writing about and teaching these things:

It's true that teaching Monads, Applicatives, and Functors can be tricky and there are a lot of articles that end up doing more harm than good- either by teaching things that are outright incorrect, or more often, teaching people a particular way to use them but setting people up for a lot of trouble when they run across uses that diverge significantly from the mental model they've built up.

Functions are a classic example of this. There useful definitions of Functor, Applicative, and Monad for functions, and depending on the mental model you've built up they can be either fairly easy to understand or very difficult to understand. This ends up being a big problem because Applicative and Monadic functions are so pervasive, but they are incomprehensible if your stuck in the traditional data structure mental model. IO is a great example of this- it's really just a specialized State, but it can be really hard to understand how it works if you're thinking about data structures. Parsers are another good example.

I generally prefer to start people off with the "monad-as-computation" mental model, roughly "An `m a` is an m-computation that can have side effects and when evaluated returns a value of type a", where Maybe are computations that could fail, Lists are computations that can return multiple times, and IO are computations with all of the normal IO side effects.

Starting with IO has the nice benefit that you can also help people come to terms with monadic IO as a means of dealing with lazy evaluation. It's a good gateway both into helping people come to terms with the challenges of lazy IO, and it also helps to provide a concrete motivation for IO in haskell that doesn't result in people going off thinking that Monads are a hammer and every problem in the world is a nail.

From there, I think it's helpful to talk not just about bind but also join. Showing someone how to implement join in terms of bind and vice versa is a nice thing to do early because it helps to differentiate Monad from Applicative and it demystifies the "a monad is a monoid in the category of endofunctors" thing a bit (not that I'd proactively bring that up when teaching someone how to use them).

I like to characterize the high level difference as something like "Monads are computations that can _call out to_ other computations and integrate their results", "Applicatives can run computations in parallel and combine the resulting structures / side effects", and "Functors allow you to lift pure functions into a computation". At each step, highlighting both how you are getting less powerful (because you can implement functors in terms of applicatives, and applicatives in terms of monads, but not the other way around), and how having less power can help you reason better about your programs (pros/cons of applicative vs. monadic parsers are a good example here).

Finally, I think it's important early on to make sure your reader understands higher kinded types. A lot of people are used to languages with generics, but many of those languages aren't expressive enough to let you express something like Functor, and people often lack practice in thinking about something like `Maybe` separately from `Maybe Int` or `Maybe a`.

In the end, I think these things really aren't that complicated, but they are built on a different view of programming that a lot of readers have the first time they encounter them, and the best approach isn't to translate the concepts into something people are already familiar with. Instead, I think you need to help the reader adapt their mental model. It's a harder path, but one that I think pays off more in the long run.

Re: Functors, Applicatives, and Monads

#15

Earlier quoted context omitted.

Bartosz Milewski argues that we can think of functions etc. as containers as well, if you check out his YouTube lectures on Category Theory for Programmers. Lists and functions "contain" a type.

A list [b] is a container for bs indexed by integers. A function a->b is a container for bs indexed by as.

[b] is more like a blueprint for a container, and a->b is more like an assembly line of containers.

Re: Functors, Applicatives, and Monads

#16

I feel like Haskell is easier to use than it is to explain, and in my experience a lot of these kind of tutorial / explanations actually make things seem harder and more complicated than just working with the concepts and observing what they do. (This one included.)

Why are there so few practical, example and code driven tutorials? I've never run across a succinct "build Twitter with Haskell" in the wild.

Re: Functors, Applicatives, and Monads

#17
post #12
post #6

For some reason everyone likes to talk about Monads, but really the other types here are just as interesting. For example, Applicatives are less dynamic than Monads in that you can't `flatMap`/`bind` to decide on the "next" thing to evaluate based on the previous value, but in exchange you get a "static" tree (or graph) of Applicatives that lends itself much better to static analysis, optimization, parallelism, and s…

> Monads seem to have this strange aura around them that attracts certain kinds of personalities Historical accident. There was a time, not very long ago, when we didn't know applicative functors were a useful separate subset of monads. We thought full monads were needed for all the neat things that applicatives are sufficient for. During this time, lots of ink was spilled over monads. Had we invented applicative fun…

Indeed it was not long ago that in the language there was no relationship at all between the Applicative class and the Monad class. And then one release Applicative was made the superclass of Monad. That's the reason why we have sequence and sequenceA, sequence_ and sequenceA_, liftM and fmap, ap and , liftM2 and liftA2, return and pure, traverse and mapM etc. All these pairs of functions do the same thing but are duplicated for historical reasons.

This historical accident has, IMO, made the language harder to teach.

Re: Functors, Applicatives, and Monads

#18

I feel like Haskell is easier to use than it is to explain, and in my experience a lot of these kind of tutorial / explanations actually make things seem harder and more complicated than just working with the concepts and observing what they do. (This one included.)

I'm not familiar with Haskell and am really, really struggling to follow the article.

In the case of the functor, the author doesn't explain in technical, specific enough terms the difference between "open the box, extract the value out of it, apply the function, and put the result back in a box" and "apply a function to a box directly; no need to perform all the steps ourselves." I have no idea what 'apply a function to a box' even means.

> That’s the essence of functors: an abstraction representing something to which we can apply a function to the value(s) inside

The error in this sentence garbles its meaning beyond recovery. "We can apply a function" governs two prepositional phrases that are semantically and syntactically identical: "to which;" "to the value(s) inside." There's no way to resolve the meaning of one without rendering the other incoherent.

Re: Functors, Applicatives, and Monads

#20

Earlier quoted context omitted.

Bartosz Milewski argues that we can think of functions etc. as containers as well, if you check out his YouTube lectures on Category Theory for Programmers. Lists and functions "contain" a type.

A list [b] is a container for bs indexed by integers. A function a->b is a container for bs indexed by as.

> A function a->b is a container for bs

Anecdotally, this is one of those things that's trivially true to some people, but really hard for other people to internalize. I think it's why the "container" can lead people astray- if you haven't internalized the idea of functions as being indexed by their argument, it's a really mind twisting thing to try to make that leap.

Post reply on HN