People love to talk about the upsides and the fun and what you can learn from Haskell.
I am one of these people.
People are much more reluctant to share what it is that led them to the conclusion that Haskell isn't something they want to use professionally, or something they can't use professionally. It's a combination of things, such as it just generally being less energizing to talk about that, and also some degree of frankly-justified fear of being harassed by people who will argue loudly and insultingly that you just Don't Get It.
I am not one of those people.
I will share the three main reasons I don't even consider it professionally.
First, Hacker News has a stronger-than-average egalitarian streak and really wants to believe that everybody in the world is already a developer with 15 years of experience and expert-level knowledge in all they survey from atop their accomplished throne, but that's not how the real world works. In the real world I work with coworkers who I have to train why in my Go code, a "DomainName" is a type instead of just a string. Then, just as the light bulb goes off, they move on from the project and I get the next junior dev who I have to explain it to. I'm hardly going to get to the point where I have a team of people who are Haskell experts when I'm explaining this basic thing over and over.
And, to be 100% clear, this is not their "fault", because being a junior programmer in 2024 is facing a mountain of issues I didn't face at their age: https://news.ycombinator.com/item?id=33911633 I wasn't expected to know about how to do source control or write everything to be rollback-able or interact with QA, or, well, see linked post for more examples. Haskell is another stack of requirements on top of a modern junior dev that is a hell of an ask. There better be some damn good reasons for me to add this to my minimim-viable developer for a project. I am not expressing contempt for the junior programmers here from atop my own lofty perch; I am encouraging people to have sympathy with them, especially if you also come up in the 90s when it was really relatively easy, and to make sure you don't spec out projects where you're basically pulling the ladder up after yourself. You need to have an onboarding plan, and "spend a whole bunch of time learning Haskell" is spending a lot of your onboarding plan currency.
Second, while a Haskell program that has the chef's kiss perfect architecture is a joy to work with, it is much more difficult to get there for a real project. When I was playing with Haskell it was a frequent occurrence to discover I'd architected something wrong, and to essentially need to rewrite the whole program, because there is no intermediate functioning program between where I was and where I needed to be. The strength of the type system is a great benefit, but it does not put up with your crap. But "your crap" includes things like being able to rearchitect a system in phases, or partially, and still have a functioning system, and some other things that are harder to characterize but you do a lot of without even realizing it.
I'd analogize it to a metalworker working with titanium. If you need it, you need it. If you can afford it, great. The end result is amazing. But it's a much harder metal to work with for the exact same reason it's amazing. The strength of the end part is directly reflected in the metal resisting you working with it.
I expect at a true expert level you can get over this, but then as per my first point, demanding that all my fellow developers become true experts in this obscure language is taking it up another level past just being able to work in it at all.
Finally, a lot of programming requirements have changed over the years. 10-15 years ago I could feasibly break my program into a "functional core" and an external IO system. This has become a great deal less true, because the baseline requirement for logging, metrics, and visibility have gone up a lot, and suddenly that "pure core" becomes a lot less appealing. Yes, of course, our pure functions could all return logs and metrics and whathaveyou, and sure, you can set up the syntax to the point that it's almost tolerable, but you're still going to face issues where basically everything is now in some sort of IO. If nothing else, those beautiful (Int -> Int -> Int) functions all become (Int -> Int -> LoggingMetrics Int) and now it isn't just that you "get" to use monadic interfaces but you're in the LoggingMetrics monad for everything and the advantages of Haskell, while they do not go away entirely, are somewhat mitigated, because it really wants purity. It puts me halfway to being in the "imperative monad" already, and makes the plan of just going ahead and being there and programming carefully a lot more appealing. Especially when you combine that with the junior devs being able to understand the resulting code.
In the end, while I still strongly recommend professional programmers spend some time in this world to glean some lessons from it that are much more challenging to learn anywhere else, it is better to take the lessons learned and learn how to apply them back into conventional languages than to try to insist on using the more pure functional languages in an engineering environment. This isn't even the complete list of issues, but they're sufficient to eliminate them from consideration for almost every engineering task. And in fact every case I have personally witnessed where someone pushed through anyhow and did it, it was ultimately a business failure.
> I'd analogize it to a metalworker working with titanium. If you need it, you need it. If you can afford it, great. The end result is amazing. But it's a much harder metal to work with for the exact same reason it's amazing.
What a beautiful, succinct analogy. I'm stealing this.
> I'd analogize it to a metalworker working with titanium. If you need it, you need it. If you can afford it, great. The end result is amazing. But it's a much harder metal to work with for the exact same reason it's amazing. The strength of the end part is directly reflected in the metal resisting you working with it.
I’d say you missed one of the main points of Haskell and functional programming in general.
The combinator is the most modular and fundamental computational primitive available in programming. When you make a functional program it should be constructed out of the composition of thousands of these primitive with extremely strict separation from IO and multiple layers of abstraction. Each layer is simply composed functions from the layer below.
If you think of fp programming this way. It becomes the most modular most reconfigurable programming pattern in existence.
You have access to all layers of abstraction and within each layer are independent modules of composed combinators. Your titanium is given super powers where you can access the engine, the part, the molecule and the atom.
All the static safety and beauty Haskell provides is actually a side thing. What Haskell and functional programming in general provides is the most fundamental and foundational way to organize your program such that any architectural change only requires you replacing and changing the minimum amount of required modules. Literally the opposite of what you’re saying.
The key is to make your program just a bunch of combinators all the way down with an imperative io shell that is as thin as possible. This is nirvana of program organization and patterns.
I'm well aware of functional programming as focusing on composition.
One of the reasons you end up with "refactoring the entire program because of some change" is when you discover that your entire composition scheme you built your entire program around is wrong, e.g., "Gee, this effects library I built my entire code base around to date is really nifty but also I can't actually express my needs in it after all". In a conventional language, you just build in the exceptions, and maybe feel briefly sad, but it works. It can ruin a codebase if you let it, but it's at least an option. In Haskell, you have a much bigger problem.
Now filter that back through what I wrote. You want to explain to your junior developer who is still struggling with the concept of using things other than strings why we have to rewrite the entire code base to use raw IO instead of the effects system we were using because it turns out the compilation time went exponential and we can't fix it in any reasonable amount of effort? How happy are they going to be with you after you just spent a whole bunch of time explaining the way to work with the effects system? They're not going to come away with a good impression of either Haskell or you.
> "Gee, this effects library I built my entire code base around to date is really nifty but also I can't actually express my needs in it after all"
This is why I recommend IO-based effect systems like Bluefin and effectful. If you find that you get stuck you always have the escape hatch of just doing whatever you want in IO. Maybe feel briefly sad, but it works.
I am one of these people.
People are much more reluctant to share what it is that led them to the conclusion that Haskell isn't something they want to use professionally, or something they can't use professionally. It's a combination of things, such as it just generally being less energizing to talk about that, and also some degree of frankly-justified fear of being harassed by people who will argue loudly and insultingly that you just Don't Get It.
I am not one of those people.
I will share the three main reasons I don't even consider it professionally.
First, Hacker News has a stronger-than-average egalitarian streak and really wants to believe that everybody in the world is already a developer with 15 years of experience and expert-level knowledge in all they survey from atop their accomplished throne, but that's not how the real world works. In the real world I work with coworkers who I have to train why in my Go code, a "DomainName" is a type instead of just a string. Then, just as the light bulb goes off, they move on from the project and I get the next junior dev who I have to explain it to. I'm hardly going to get to the point where I have a team of people who are Haskell experts when I'm explaining this basic thing over and over.
And, to be 100% clear, this is not their "fault", because being a junior programmer in 2024 is facing a mountain of issues I didn't face at their age: https://news.ycombinator.com/item?id=33911633 I wasn't expected to know about how to do source control or write everything to be rollback-able or interact with QA, or, well, see linked post for more examples. Haskell is another stack of requirements on top of a modern junior dev that is a hell of an ask. There better be some damn good reasons for me to add this to my minimim-viable developer for a project. I am not expressing contempt for the junior programmers here from atop my own lofty perch; I am encouraging people to have sympathy with them, especially if you also come up in the 90s when it was really relatively easy, and to make sure you don't spec out projects where you're basically pulling the ladder up after yourself. You need to have an onboarding plan, and "spend a whole bunch of time learning Haskell" is spending a lot of your onboarding plan currency.
Second, while a Haskell program that has the chef's kiss perfect architecture is a joy to work with, it is much more difficult to get there for a real project. When I was playing with Haskell it was a frequent occurrence to discover I'd architected something wrong, and to essentially need to rewrite the whole program, because there is no intermediate functioning program between where I was and where I needed to be. The strength of the type system is a great benefit, but it does not put up with your crap. But "your crap" includes things like being able to rearchitect a system in phases, or partially, and still have a functioning system, and some other things that are harder to characterize but you do a lot of without even realizing it.
I'd analogize it to a metalworker working with titanium. If you need it, you need it. If you can afford it, great. The end result is amazing. But it's a much harder metal to work with for the exact same reason it's amazing. The strength of the end part is directly reflected in the metal resisting you working with it.
I expect at a true expert level you can get over this, but then as per my first point, demanding that all my fellow developers become true experts in this obscure language is taking it up another level past just being able to work in it at all.
Finally, a lot of programming requirements have changed over the years. 10-15 years ago I could feasibly break my program into a "functional core" and an external IO system. This has become a great deal less true, because the baseline requirement for logging, metrics, and visibility have gone up a lot, and suddenly that "pure core" becomes a lot less appealing. Yes, of course, our pure functions could all return logs and metrics and whathaveyou, and sure, you can set up the syntax to the point that it's almost tolerable, but you're still going to face issues where basically everything is now in some sort of IO. If nothing else, those beautiful (Int -> Int -> Int) functions all become (Int -> Int -> LoggingMetrics Int) and now it isn't just that you "get" to use monadic interfaces but you're in the LoggingMetrics monad for everything and the advantages of Haskell, while they do not go away entirely, are somewhat mitigated, because it really wants purity. It puts me halfway to being in the "imperative monad" already, and makes the plan of just going ahead and being there and programming carefully a lot more appealing. Especially when you combine that with the junior devs being able to understand the resulting code.
In the end, while I still strongly recommend professional programmers spend some time in this world to glean some lessons from it that are much more challenging to learn anywhere else, it is better to take the lessons learned and learn how to apply them back into conventional languages than to try to insist on using the more pure functional languages in an engineering environment. This isn't even the complete list of issues, but they're sufficient to eliminate them from consideration for almost every engineering task. And in fact every case I have personally witnessed where someone pushed through anyhow and did it, it was ultimately a business failure.