I was having a conversation with another software engineer the other day and they asked for my thoughts on various programming languages and paradigms. The topic came up because I mentioned that Python3 supported Optional types which are a popular feature in many functional programming languages like Haskell, OCaml, and F#.
Conversations like these often turn into statements about the general merits of technology x or y. It can be very tempting to draw specific conclusions. I’ve heard many of these, and have held them myself in the past. Conclusions like “Functional programming makes parallelism easier” or “Static typing reduces bugs” are seductive in their simplicity, but they smooth over the massive surface area that Software Engineers find themselves navigating these days.
Personally, I really enjoy functional programming. I enjoy that functional programming often allows you to express very complicated processes as simple chains of operations on immutable data. I learned Haskell many years ago by watching Erik Meijer’s Channel 9 Lecture Series and reading along with Graham Hutton’s Programming in Haskell. I worked at a few companies where I was paid to write Scala, and we used the Finagle RPC System which uses a lot of functional programming concepts.
These days, most of the code I write at work is in Go which is about as imperative as you can get. Functions are first class objects, and Go 1.18 has introduced generics, but that’s about it as far as functional programming concepts go (sure, generics aren’t specifically a functional programming concept, but I’d argue that they’re kind of necessary in a statically typed language if you want to do any kind of functional programming). Go does, however, have a lot of power in its simplicity, in its treatment of errors as types, and the ability to model concurrency through channels and goroutines.
I’m also really enjoying learning Rust which is a multi-paradigm language particularly well suited for systems programming. For that matter, I’ve also been brushing up on the last 10 years of developments in C++ as part of doing the assignments in a CMU databases course that I’m following.
So, what languages and paradigms do I prefer, and why? Here’s the kicker: I don’t care. I really don’t. There are so many factors that go into whether a particular programming language is a good choice for a team or project. Factors such as, what does the team have experience with? What problems are you solving? What support is there for various types of tooling and infrastructure at your organization? What conventions already exist and is it worth the friction of going against those conventions? Sometimes it is worth it, sometimes not. Only you and your team know enough context to make those sorts of decisions. What’s the ecosystem for a language like? Some languages have found niche status in certain domains, for instance with Python and ML.
Changes have to be considered in context. There are merits, for instance, to using Optional types in Python3, that’s why they were added. But if a Python code base that you’re working with has a convention of throwing exceptions when a function cannot return a specific concrete type, which is going to cause more friction to use? A team can decide to adapt to changes in a language and start moving towards new conventions, but that’s a highly contextual decision that shouldn’t be made in isolation. Similarly, if an organization has really great infrastructure and tooling for developing services and applications in JVM based languages, there’s a huge benefit to sticking to those.
Learning new programming languages can open you up to new areas for development. For instance, there’d be quite a lot of friction to doing systems programming in a language that doesn’t support low level memory management, whereas a language like Rust or C or C++ will make this easier. Most enjoyably, learning a new programming language can broaden how you think about programming. This is wonderful. I’m also so happy that we live in a time with such rich diversity of problems that need solving. We’re not all working on transaction processing systems at banks and business intelligence applications like we seemed to be when I started my career in the early 2000s (just after the dot com bubble burst). Just as in databases, one size does not fit all.
I also think that cross-polination between language communities is a great thing. Features like list comprehensions, Optional types, Futures and Promises have found their way from one language to another. There’s an interview where Simon Peyton Jones talks about Haskell as a vessel for this kind of cross-polination that I find really refreshing, especially in a world where we can get caught up in language bigotry and holy wars. Have a watch: