In my experience, the performance tooling is the biggest “gap” for Haskell in Production.
For anything else, I have only ever gotten frustrated making changes to large projects written in other languages. Refactoring in Haskell, IMHO, is the biggest win. Adding features and changing requirements with Haskell is often a breeze, where as in JavaScript or Python I would second guess almost every change I try to make because there’s zero guarantees (though TypeScript has helped a bit there) and I’m just there hoping we have enough tests (and the right ones) to catch anything that I might break while changing code.
Every other “better” production language [1] I’ve had a chance to use in prod lacks a repl and therefore results in bad code tho. It becomes a pain to truly write small modular code so instead you write black boxes that do a lot and integration test their behavior.
So I confidently say those languages have bad tooling
[1] Golang, Java, Rust. Dynamic langs have repls but the code is always automagic framework soup and hard to hold in your hand.
Same here. For me STM and async exceptions are the killer features of Haskell. But there are too many people who hate async exceptions (or exceptions in general) to brush it off. I mean, there has to be something about it.
It’s hard to say anything specific without particular cases, but for me the point of no returns was when I figured that most bugs related to handling async exceptions are not specific to async exceptions, and are just general exception handling bugs. And most exception handling bugs are just error handling bugs.
I.e. if you are mishandling async exceptions, there is a high change you are mishandling sync exceptions as well. It’s just that async exceptions trigger the bug more often.
I formed this opinion after having to deal with golang for a few years at work. In go, the only pattern at your disposal to deal with situations that would be trivially solved with an asynchronous exception is to use event polling.
The basic idea is that you have a “rendez-vous” point in your application logic where you check for certain conditions, like “ctrl-c”, or “should stop now”. The problem with this approach is that it is not composable. You need to create as many “rendez-vous” as places in your code you would wish to abort execution. In practice, developers don’t want to do this because it makes the code less readable, and forces them to think about this problem everywhere. What ends up happening is that programs tend to have large blocking chunks of logic. You cannot escape from those blocks until they finish, which could be a very long time, or never.
If you ever had to do ctrl-c while running “terraform apply” only to wait a really long time to realize it will not stop, you know what I’m talking about.
That was a source of bugs in my company. Things got stuck more often than we wanted to. The only solution was to thread context structs and check for cancellation in individual threads or tasks. I’m not saying it was impossible to fix, it was just very inconvenient and took a long time to get right.
With Haskell the solution seems too magical. You just have to get the exception handling right once, and then it works for everything: timeouts, allocation limits, ctrl-c, thread racing…
Sorry for being too dense, but I’m not sure I understand the question. Note: English is not my native language, so it’s possible I’m misreading your comment.
I can only offer a quote from the Simon’s post mentioned above:
In the vast majority of our Haskell codebase we don’t need to worry about them at all.
That matches my experience as well. You don’t usually use async exceptions at all. You don’t even worry about them most of the time. But you use System.Timeout.timeout, Control.Concurrent.Async.race etc which internally rely on async exceptions.
I’d be happy to answer a specific question, like “how to use async exceptions is this particular situations” though.
Ever use Go? And leak goroutines? That’ll take down your entire server fleet.
And to avoid it you have to remember to use idioms to make sure they don’t leak - impossible to bracket or otherwise abstract?
That’ll make you appreciate the existence of async exceptions. I think the hate for them is due to the loudest Haskellers being library authors, where async exceptions are added complexity and potential footguns.
I think this video is one of the most important ones about Haskell performance tooling anyone could watch right now. I recommend people watch it beginning to end. It shows how far we’ve come and what’s currently available.