What are the strengths of Haskell that could help us to convince IT managers to consider choosing Haskell when:
- starting up a new project?
When starting a new project, one overriding concern is often the time-to-market, because it makes sense to prove that there is a market for a product before spending too much effort into it. As such, a language such as Haskell, in which the compiler stops you every time you make a small mistake, might seem like a bad fit for that goal. Common objections include:
Writing all those types will waste time
Debugging type errors will waste time
Designing abstractions will waste time
This third objection isn't actually common at all, but I added it to the list because I again want to show which downsides are real and which ones are misconceptions. And in this area, there are definitely a lot of misconceptions! Neither of the two common objections is well-founded, and the one objection which is well-founded isn't common. Anyway, let's examine each one.
The usual rebuttal is that Haskell has good type inference, and so no time needs to be wasted writing types. My rebuttal, on the contrary, is that I think writing the type signatures saves more time than it wastes! A type signature contains some, but not all the relevant elements of the problem. Haskell's type system is very expressive, allowing us to write type signatures which focus on the parts of the problem which we think are relevant. This helps me to structure my understanding of the problem, which in turn allows me to waste less time looking for a solution.
That is, if I need to write a solution at all! As Conor McBride explains, it is not type inference which we should focus on (given a program, derive its type), but program inference: given a type, derive the program which has that type. Haskell accomplishes a small part of this vision via instance resolution. It only makes sense to even consider program inference in a language which has precise-enough types that the only program which has the goal type is indeed the program we want to execute.
Part of the upfront cost of learning Haskell is that while learning, we make mistakes, and so we spend a lot of time figuring out how to resolve type errors. At the beginning. As Haskellers become more familiar with the type system, we learn to predict type errors well enough that we no longer need to wait for the compiler to tell us that we've made a mistake, we get an intuitive feel for which expressions would not type check, and also which expressions would work at the cursor's current position. In a sense, we get well-typed expressions as auto-completion suggestions in our head as we type! This is called "following the types", and it means that typed programs can be written more quickly than if we didn't have types to guide us. I'm sure one day Haskell IDEs will make this approach more accessible.
When following the types, we still have to consider a few potential expressions, and we have to choose the correct one among them. Well, another aspect of precise types is that they can be designed to encourage the programmer to make the choice which is the most likely to be correct, by intentionally making less common expressions harder to type. To quote Conor McBride again: "types [warp] our gravity so that the direction we need to travel becomes 'downhill'". Thanks to this guidance, we waste less time exploring incorrect solutions.
These tricks are great when we're using precise types and abstractions which come from somebody else's library, but when designing our own... well, they're still great, and they can be even better since the abstractions are adapted to our particular situation, but it can take a while to get them right. Libraries and abstractions save time in the long run: it takes time to write them, but then they allow us to save time each time we use them. As I have learned at my expense, for a short project such as an MVP, a project-specific abstraction might not get used often enough to pay for the cost of developing that abstraction. For this reason, I recommend sticking to existing libraries and abstractions when writing short Haskell programs. But I must say, when I spot a pattern in my code, it can be hard to resist!
- revisiting an existing project?
“Revisiting”? Rewriting a program is rarely worth the cost, but if you are considering a rewrite, it must be because the existing program has became unmaintainable. Because changing one part of the program causes some random other part of the program to misbehave. Because there are parts of the program which nobody understands and which no-one dares to delete or rewrite for fear of breaking something.
Well, it’s definitely possible to write hard-to-understand code in Haskell, but there are a number of factors which make Haskell code easier to maintain.
Pure functions cannot mysteriously cause some random other part of the program to misbehave.
Making a change to a pure function certainly has some effect on the rest of the program, otherwise we wouldn’t bother changing it, but the part of the program which is affected isn’t random, it is always the part which uses the value returned by the function. This is in stark contrast with object-oriented programs, in which all the code which subsequently uses the object which hosts the method, the objects received by the method, and the objects indirectly-referenced by those can be affected. Which is, in turn, less code than with programs which commit the sin of using global variables.
So changing a pure function is never going to have mysterious effects on some far-away part of the program. Similarly, a pure function whose output is never used is definitely dead code, so it can be removed without fear. And if a pure function is rewritten, as long as it outputs the same thing as the old version, we know we aren't going to break anything.
Of course, not every Haskell function is a pure function. IO actions which share state, via files or IORefs or otherwise, do have the potential to affect all the other IO actions which use that shared state. For this reason, IO is often vilified by Haskell introductions, but unfortunately many large codebases end up using it all over the place anyway, in a monad stack containing IO. If possible, I recommend spending the extra upfront cost of making sure the team learns about the alternative ways of structuring their top-level code, e.g. using the parse-transform-print idiom, the Onion Architecture, FRP, etc.
The type system guides you towards a successful refactoring.
If you change an API in a way which breaks its semantics, make sure the new API has a different type than the old API. This way, every call to that API will now fail to typecheck, so you once you fix all the type errors, you can be certain you have fixed all the call sites.
This is nice, but you can do that in any statically-typed language, this isn't specifically an advantage of Haskell. And yet, Haskellers often mention those painless refactoring as a key benefit of using Haskell. I think it might be because with precise types and pure functions, changing the types when you change the semantics is what happens by default, so you get this benefit even if you haven't learned this trick of changing the type to find all the callers.
I think that a more important reason why refactorings are painless in Haskell is that the community puts a lot of emphasis on "laws" which tell you when one piece of code is guaranteed to be equivalent to another. As a result, when changing all the call sites to match the new API, there are safe transformations we can perform to massage that code into the newly-required shape, without having to worry about understanding the details of each individual call site.
So, if you have an existing project which you're considering to rewrite, using Haskell for the new version should be a good way to prevent this new version from also becoming unmaintainable and requiring yet another rewrite.
What would be the factors that may be discouraging project managers from adopting Haskell?
See question 1.
- Do you think that adopting Haskell in wider scale in industry will be a good thing? Will it contribute to enhanced general human productivity and to a better world?
Haha, that’s quite a grandiloquent way of saying it! I do believe that Haskell is a more productive language, for all the reasons I listed earlier, but I should say that its enhanced productivity is not the reason Haskell is my language of choice. Purely-functional programming is very different from object-oriented programming and other forms of imperative programming, and while this difference can be seen as an obstacle while learning the language, in my case I discovered that I liked the purely-functional approach much better, as it fits with my way of thinking. Going back to use an imperative language made me sad, because I could no longer express myself in the same way in which I am thinking, I instead have to translate it into a form the imperative language can understand. As a result, while it would make me happy to see Haskell be more widely adopted, I don't know whether others would also find that purely-functional programming matches the way they think, or if making them use Haskell would make them feel sad, because they'd now have to translate their imperative thoughts into a form Haskell can understand.
In the end, if the goal is to improve productivity, I think programmers are a more important part of the equation than the language they use. I have used both imperative and functional languages at work and for personal projects, and I have found that when I participate in game jams, I am a lot more productive than I am at work, regardless of the language I use, because I am very passionate about the game I am creating.
Anecdotally, I was frustrated by how long it took to implement each feature when I was working in C++, and I am still frustrated by how long it takes to implement features now that I am lucky enough to use Haskell at work. It's definitely better with Haskell, but using it didn't boost productivity by a life-changing amount. It's the other aspects of Haskell which were life-changing, at least for me :)
Finally, is it acceptable to include your name and your words in our article that will be viewable publicly online?
Of course. Happy New Year, 明けましておめでとうございます!
Thank you Samuel!!