A Little Fewer Bugs…
…Already Helps a Lot!
In the last couple of years, i.e. since 2020 when I finally got my bachelor’s degree in Computer Science, I was mostly interested in functional programming languages: Erlang, Elixir, Scheme, Clojure, and Haskell. A year before, in 2019, I was trying to learn Rust, but I didn’t get very far. Yet another year before, in 2018, I spent a lot of time with Go, which was quite productive.
I wouldn’t want to miss any of it. But when I look back at those languages now, I see them mostly as steps on a ladder leading me higher and higher. With each step, it got a bit harder to learn. But there is also a payoff: Each programming language in this succession—Go, Rust, and then the functional ones with Haskell on the very top—eliminates a certain class of problems for a programmer coming from a mainstream language such as Java and C# (strongly typed) or Ruby and Python (dynamically typed):
- Go’s Communicating Sequential Processes (CSP) almost eliminates race conditions if one sticks to channels and avoids memory sharing.
- Rust, which later incorporated “Go-style” concurrency (i.e. CSP), makes memory races impossible thanks to its borrowing mechanism, move semantics, and compiler checks.
- The functional programming languages I tried out all use Persistent Data Structrures, i.e. lists, sets, maps, and records that do not change, but return a new version of themselves as manipulation operations are performed on them. State isn’t changed, but safely advanced. They also come with more safe concurrency mechanisms such as Software Transactional Memory (Clojure) and the Actor Model (Erlang/Elixir), which eliminate data races using different approaches than Go and Rust.
- And finally, Haskell with its powerful type system, which also inspired Rust and later on also the Parametric Polymorphism in Go (vulgo “generics”), comes with a compiler preventing a lot of possible runtime problems you’d suffer from in other programming languages. (As far as I know, Haskell’s type system was inspired by ML, so Rust might also have been inspired directly by ML.)
While Go is small and therefore easy to get into, Rust enforces way more discipline and therefore requires the programmer to learn some additional concepts. Even though the need to “forget everything you know about programming” first to get into functional languages is a myth (eloquently debunked by Russ Olsen), there is hard work required to get used to (tail) recursion and higher-order functions instead of loops. And learning Haskell does require a modicum of masochism. (As a Python programmer, learnig to write Quick Sort in Haskell using a list comprehension was way easier than grasping a “Hello, World!” program using the IO Monad.)
Each subsequent step on the ladder is a bit harder to climb than the one before. But is the additional effort really worth it, as each successive step is so much harder to take, and only eliminates very few kinds of bugs compared to the language(s) a step further down the ladder?
I think it is!
Bugs are not only annoying by themselves, but cause further annoyance down the line:
- A bug not only needs to be fixed, but the fix also needs to be shipped. This requires a well-commented commit, an entry to the changelog, running a testing pipeline, a release (building and publishing artifacts)—and of course updates on the user’s side.
- Dependencies seldom come alone, and each updated dependency can break the already shaky dependency tree one is so often confronted with nowadays.
- An updated dependency requires re-testing and, in the worst case, modification, because sometimes client code does rely on the erroneous behaviour of a dependency. And each updated dependency requires software releases downstream, which again might cause further disruption.
The fewer bugs one has, the fewer disruptions will be caused down the line. Not only for ourselves, but also for the people depending on our software. This is not a great insight, but almost a truism. But if learning a more advanced language allows you to eliminate entire classes of bugs, the learning effort certainly will pay of sooner or (probably) later.
So I’m (once again) trying to learn Haskell so that I can hopefully reap the benefits of a little fewer bugs in a few years’ time. In the meantime, I’ll be trying to be productive with Go, Ruby, Python, JavaScript and the like. But Haskell is my ultimate goal. At least, performing intellectually demanding work is said to stave off dementia. And judging by action movies, insanity looks way more appealing to me than dementia…