If I were to travel back in time 4 years ago, and tell my old self that Haskell was starting to lose its shine, I wouldn’t believe it. I grew up on Haskell, my appetite for category theory was whetted by Haskell, my biggest programming projects have been in Haskell, and my dream job was to work at a company that used Haskell.
But now, I find myself simply not as excited about Haskell as I used to be. What changed?
I think there are a couple things. I think one primary factor is that the kind of programming that Haskell really excels in; i.e. creating abstract, correct interfaces for things, is just not a type of programming that’s interesting to me anymore. When I wanted to work on software as a career, a language that allowed incredible facilities in not repeating yourself was very useful. Types that ensure correctness of data interchange, or lenses that allow access to complicated data structures are all very well for implementing, say, a compiler, or complicated business logic in a web backend. However, my interests in software are now primarily as a scientific/mathematical tool. Numerical algorithms can be done in Haskell, but they don’t really gain much benefit from the type system, and they also don’t have great library support.
No doubt Haskell could be made into the kind of language to use for the problems that I am interested in, but given the choice between working on the problems that interest me, and working on infrastructure for the problems that interest me, I would rather work on the problems that interest me. The general feeling that I have is that Haskell is a great tool for a software engineer, but I don’t want to be a software engineer, I want to be a mathematician that sometimes uses computers.
But there is another reason too. While I think that Haskell is still a great language, it is close to 30 years old at this point. It manages to stay fresh and relevant with an ever-growing list of extensions, and constantly changing best practices and libraries (which is itself a problem…), but it would be very sad if we as a society of programmers had failed to surpass it in any respect with any of the programming languages that have had the advantage of starting from a clean slate. In this post, I want to talk about these successor languages, and what I think about them.
Surprisingly, one of Haskell’s great strengths is as a systems language. It manages to be much faster than most dynamic languages, while allowing a much higher-level interface than traditional systems languages like C (obviously). One great example of a “systems” program written in Haskell is git-annex. It is a git addition that adds tracking of large files, and was my primary backup system for a long time (I eventually decided that I didn’t need the additional power from it, and was better served by a more seamless solution).
However, in 2020, the premier system’s language is surely Rust. It would be unfair to compare the performance of Rust and Haskell, because Haskell is optimized for things other than performance. That being said, Rust is faster and lower-latency than Haskell, both of which are important. However, it also has a great type system, unlike C or C++ (we don’t talk about Go…). The type system in Rust is obviously very influenced by the type system of Haskell, but they also implemented “ownership” which allows for the killer feature garbage-collection free automatic memory management.
When I first started using Rust, I really missed monads. But here’s the thing. Having used lots of monads in Haskell, and read lots of blog posts about monads, I’ve learned that in systems contexts, it’s often best to just have a simple monad stack that just consists of Reader + IO (and Maybe’s and Option’s sprinkled about occasionally). Huge monad transformer stacks often raise more problems than they solve. But Reader + IO is essentially the “default monad stack” of Rust.
Rust also has some other killer features, like the ability to compile to webassembly (yes there is ghcjs, but, really, do you want to use ghcjs?) It also from the beginning was targetted towards industry, and consequentially has a much more vibrant ecosystem.
This all being said, I think it is worth looking at the features that are prominent in Haskell that ended up going to Rust
- Typeclasses (in Rust they are Traits)
- Sum types (you may take this for granted, but a lot of languages don’t have them….)
- Pervasive pattern matching
- Hindley-Mindler type inference (automatic type inference for variables)
- Pervasiveness of things being expressions rather than statements
- Parametric Polymorphism
- The feeling that once your program compiles, it will run
I think that we should recognize Rust for what it is, a child of Haskell and the Haskell community, and like all good parents, we should want it to do better than the previous generation. In as much as Haskell is the ideas that form Haskell, the success of Rust is the success of Haskell.
OK, mainstream programming languages are great, but sometimes you just want to make the perfect type-based interface to your stuff and show the imperative scrubs what a wiz-kid you are. Or alternatively, sometimes you really care that your software is correct. Or you want to concretize a new category-theory inspired design for a part of a compiler. Nowadays, the language for that is not Haskell, it is Idris.
There are about six different ways to sort of have dependent types in Haskell (types that depend on values, like a length-n) array. I don’t really fully understand any of them, and it is totally unclear to me how they work together. Presumably, there are blogs which outline the One True Way, but… it’s tough. In Idris, it just seems perfectly natural to use dependent types, like, why wouldn’t you able to have a type parameter which was a value? In many ways other than dependent types, Idris is a much cleaner language than Haskell too. And with Idris2, it has support for linear types, which allow mutability in a functional context via guarantees that nobody is going to try and use the old value. If I want to play around with a cool type system in a language that can also actually do things with the real world (i.e., unlike Agda or Coq), I would go to Idris rather than Haskell.
But Idris is undeniably Haskell’s child. The first version was written in Haskell (it is now self-hosting). They are similar in more ways than it is worth counting. Enough said.
Unlike the first two, Julia doesn’t really muscle into Haskell’s territory. Scientific computing was never really Haskell’s forte, despite there being some very cool libraries written in it, like
ad for autodifferentiation, or various array-handling packages that automatically fused consecutive array operations.
Also, Julia is a dynamically typed language. How could a filthy dynamically typed language ever claim to be Haskell’s child??
Well, for one it steals some of those cool libraries, and makes them much better! Flux is a neural networks library which essentially is just autodifferentiation + some nice utilities, and it is already competitive in my mind with TensorFlow. Julia also has StaticArrays, which integrates the size of the array into the type, and Julia has some neat fusion abilities too for making array operations really fast.
But wait, you ask, how can it do this if it’s not a statically typed language? Well, Julia is not your average dynamically typed language. It actually has a very interesting type system, a full discussion of which is beyond the scope of this post, and the focus on types as the unit of programming is (somewhat?) similar to Haskell (now I’m stretching it a little though).
The real reason I include Julia, however, is because for me personally, it has replaced Haskell as the place to do category theory. This is because of a shift of viewpoint: rather than providing a type system into which category theory can be embedded in to guide typical software engineering tasks, Julia provides a system in which computations in category theory can be carried out in an efficient way. Specifically, I’m talking about Catlab.jl. A discussion of Catlab.jl is also beyond the scope of this post, but I encourage you to check it out.
Therefore, I count Julia as a child of Haskell (or maybe, I count Catlab.jl as a child of Haskell) because the idea of organizing computation with category theory would not exist in the same way if it weren’t for Haskell.
If I could talk to the Haskell-obsessed teenager that was me four years ago, I would tell him to keep his mind open. Haskell is still great for a lot of things (compilers come to mind), but if Haskell couldn’t inspire superior successors, there wouldn’t be worthwhile ideas in Haskell. There are those on the internet who are talking about how Haskell is dying, and they may or may not be wrong. Stephen Diehl, one of my main Haskell idols, is distancing himself from the Haskell community because of Haskell’s use as intellectual eye-candy on scam cryptocurrencies, and I think that there may be a tipping point where Haskell loses the zeitgeist of being exciting, and because it never had much of a foothold to begin with in industry, slips into irrelevance. But Haskell will always live on; it had a huge impact on many programmers and many languages disproportionate to its actual use, and it will always have a special place in my heart.