Hacker News new | ask | show | jobs
by marcan_42 1544 days ago
Well, that's the first time side effects in Haskell made sense to me. Well done.

Not that I've ever seriously tried to learn Haskell, but in the past every time I've lazily come across an article about it it's always seemed like a bizarre confusing world, even though I know how functional programming (in the sense of purity) works.

Now there's just one thing missing. We all know what this style of programming is. It's asynchronous programming with callbacks. Seriously, Haskell folks, if you started with "all side effecting functions are kind of like async operations with a completion callback (the stuff people do in JavaScript all day), and then we have some syntactic sugar to make it suck less" you'd have a much easier time getting people to wrap their head around all this.

(Yes, I know the details aren't exactly the same, but drawing parallels to stuff people already know matters)

Seriously, this idea of there being a central effect dispatcher (the bit that runs `main` behind the scenes) is so eerily like the coroutine scheduler in async coroutine paradigms that I can't believe more people haven't drawn parallels between these programming styles.

5 comments

I think the problem is that people have learned the monad concept and then go overboard with it. Only monads are like async code with callbacks.

If people stuck to just explaining the IO monad specifically, which is a lot like async code with callbacks, then things would be better.

I wrote about this a few years ago: https://two-wrongs.com/the-what-are-monads-fallacy

I like the analogy with musical instruments. Very practical.

I think you're right in saying that async is not representative of all monads, but it does help with questions that many beginners have, like "How do I get the value out of the maybe/IO/your-monad-here?"

I don't think it generalises that well, unfortunately. How to get the value out of Maybe is "just pattern match it". There's nothing process-like about it at all -- it's just a plain container/wrapper.

The async analogy works for some monads like ST and IO and whathaveyou but it's not generally useful, due to the wide variety of types that are monads.

I think it's still a useful monad to look at. If you just pattern match Maybe, you're not really treating it like a monad or a functor, just as a tagged union that happens to implement a monad interface.

In contrast, there are easily understood reasons to use the monad interface of a promise/async. That's why I think it's a good monad for understanding the structure of monadic binding.

The way I see it is kinda like this, and I hope it makes some sense to others:

You can get the value out of a list... but there might be many or none. You can get the value out of a maybe... if it's there. You can even get the value out of the async function... but you'll just have to hold up what you're doing to wait for it.

This kind of (roughly) non-determinism is how I understand the concept of "effect", which is what Haskell frequently uses monads to model. A function returning a `M a` becomes "deterministic" (it always returns an `M a`) and benefits from the sort of equational reasoning that we like.

To be clear, the use of monads to represent effects is a software engineering decision and not inherent to monads.

It's definitely helped my understanding and I suspect it would help others too.

I disagree. I feel like this perspective makes sense to you because you feel more comfortable with js, not because monadic io is best explained in terms of callbacks.
While you may oversimplify a little, I think the core idea of your comment is pretty useful for learning. Unfortunately the first monads one encounters when learning Haskell are typically Maybe and IO. There are good practical reasons for this, but an early analogy to Async would probably make the idea a lot clearer.

Self-plug: I even wrote a short post about this quite recently, after making a similar comment on HN (https://frogulis.net/writing/async-monad)

When learning Monads in F# I felt like I'd seen this before. Promises in JavaScript were the first time I'd seen them. I just didn't know what they were at the time.
This perspective is interesting to me because I feel like every single comment section on HN about async syntactic sugar has a Haskell programmer complaining that the sugar is only available for 1 single type (the async type) and not ALL THE MONADS.
it's asynchronous programming with promises