The Pragmatic Programmer bible by David Thomas and Andy Hunt recommends learning a new programming language each year. Each new language comes with its own guidelines, style, devote followers, and approach to problem solving. The more languages under your belt, the more likely you’ll be able to creatively combine and convert practices from one language into another.
Bruce Tate didn’t like the recommendation of Andy Hunt and Dave Thomas. Just one language, each year? Why not seven in seven weeks? In Seven Languages in Seven Weeks, Tate not only helps jump-start seven languages, but also how to quickly learn any new programming language. Its success announced the inevitable follow-up four years later, Seven More Languages in Seven Weeks. Tate’s books not only cover the technicalities of a language but also give the reader a taste of how other programmers across broadly different communities solve complex problems.
In Fluent Forever: How To Learn Any Language Fast and Never Forget It, Gabriel Wyner—a polyglot that fluently speaks Russian, Italian, German, French, English, and less fluently even more—explains his secrets for quickly and efficiently mastering grammar. It’s been since 2015 that I read the book, so I can only remember I was impressed, but a new question arises: is learning a new language similar to learning a programming language?
Plenty of academic research exists on that topic: I could continue by citing papers that confirm this theory, but the fun doesn’t stop there, as there are of course also papers that deny it. Theory aside, a few common critical thinking fallacies occur in both spoken and programming language learning. In a recent paper called Here we go again: Why is it difficult for developers to learn another programming language, researchers identified that what they call cross-language clashes also occurs while learning a new programming language.
For instance, suppose you’re a Java developer, and you’re learning Kotlin. Here’s a Kotlin expression for you:
val boundsBuilder: LatLngBounds.Builder = LatLngBounds.Builder()
Would it be possible to rewrite the above? You could leverage your JDK experience: sure enough, type inference might also exist in Kotlin, so we could loose the
val boundsBuilder = LatLngBounds.Builder()
No big surprises here—Kotlin also runs on top of the JVM. As a person speaking Dutch, I sometimes make faster progress learning German because they’re both germanic languages that share (some) of the same syntax and etymological origins. With emphasis on some. My grandmother speaks a local Flemish dialect with lots of German loanwords. This is called cross-language facilitation: precisely because you know one language, you’re making much faster progress in the other.
Cross-language interference also occurs in spoken languages. The Spanish word “embarazada” looks like “embarrassed” but I’d advice against using it in the same sentence since it means “pregnant”. Whoops. There are plenty of such silly examples you can use to keep boring family members entertained at parties.
While we’re at the subject of prejudices: learning a new programming language doesn’t mean the culture and community that drives that language are the same. In my view, we as developers are all too eager to learn something new and shiny, without first considering a few important things:
- Will this language solve my problem? What is its primary purpose?
- What is the infrastructure like?
- How active is the language?
- How open is the community?
- What is the language’s design philosophy like?
- What’s the history behind the language?
For Q1, in a spoken language, it’s obvious: to communicate with others. But not always: learning Latin will open the world to the ancient world through books by no longer relying on translations. There’s no need to jump ahead and start learning Latin to be able to say quod erat demonstrandum to your neighbor. For a programming language, this question is surprisingly difficult to answer and depends on many variables. For example, investigate languages such as Elixir if you require concurrency and message-passing at the get-go. Investigate something like Go if single native binaries are important to you. Check out JRuby, Clojure, or Kotlin if you already have critical things running on the JVM and want easy JVM interoperability. If you require a modern systems language with full control, check out Rust or Hare.
We’re easily deceived when it comes to answering Q1. For instance, Go is said to be performant because it compiles to native binaries and you can use pointers—with the ease of a garbage collector. The modern Caddy webserver, implemented in Go, is 10x faster than Apache2. But also still slower than Nginx. And are we measuring the same thing here? Of course not. Performance is (almost) never a great argument for picking one language over the other: this is an implementation issue, not a language issue. Don’t write Python code because everyone does.
For Q2, I’m thinking about unit testing (awesomely integrated in Go), remote debugging, hot code swapping (shit in C but awesome in Elixir), security features, etc. A language is never just a set of keywords called the syntax: the tools that surround it are perhaps more important than the dialect itself. Is a package manager included? How about stress tests or support for extensive debugging like Delve?
For Q3, I want the language to be future-proof and stable. I don’t want to change spelling every six months (ECMA Standard decision makers, I’m looking at you), but I don’t want to end up with a dead language. The Dutch language had a few major changes in the nineties which was difficult to grasp without a guide such as het Groene Boekje (the Green Booklet). Luckily, for spoken languages, this is less prevalent. Imagine having to change syntax every few years… Guess what we as developers have to do every few years (or every week, seven times)!
Q4 is, to me, becoming more and more important. Nowadays, I don’t want to choose a language just because I think it’s cool and will fit my purpose: I want the community to be vibrant and friendly because I might be part of it. I might want to contribute. This is a very subjective feeling, and gauging it is perhaps a bit challenging. What kind of feeling does the main website and docs radiate? When attending a conference (or viewing a few videos online), how well are speakers and questions treated? If you’re still having a hard time figuring out: check out the Oracle Java site, and now check out go.dev or elixir-lang.org. You’ll see (and feel) what I mean.
Q5 is very easy to visualize: try to generate a dependency graph of any Node project and count the amount of lines. Is it over 10? Okay, so now you know. If nobody really cares in the Node world, then chances are you or your teammates won’t either. Idioms and best practices also fall in this category. Do you like the Pythonic way to write
list(map(x)), or do you prefer
x.map().list()? How about callbacks everywhere or prepending stuff with
async and then wrapping more functions in
Promise.all()? This might not sound like a big deal, but to me, the details do matter. Provided I can choose the language, of course—most of the time, you’ll have to work with what’s already there.
For a natural language, many of these questions are organically answered and grown. But for programming languages, we can artificially construct something with a firm baseline and set of beliefs. Sure, there is a whole slew of constructed natural languages, both a priori (artistic ones such as Klingon) and a posteriori such as Interlingua. The history of these languages is very fascinating. There, I now brewed a sixth question! Perhaps we could add a Q6: what’s the history behind the language? Why was there a need to create yet another language that didn’t fit as a DSL in an existing one? Answering that question will usually say something about the purpose, culture, and design philosphy.
You see, it’s never just a case of “I’ll start investing in language
x and be a good programmer really soon!”. I even neglected to mention another important consideration: fun! Is it fun to write software in that language? Do you like it yourself? Why, or why not?
So, is learning a new language similar to learning a programming language? Yes and no. Learning a programming language comes with much more parameters to consider. French and Japanese also have a rich history and unique culture equipped to them, and by studying their culture, you’ll make studying the language easier. But a programming language evolves much quicker, comes with infrastructure, and requires thinking work to successfully marry it with a purpose. I’d dare to say that programming languages are harder to—fully—grasp. Feel free to disagree.
In the end, I tend to go with the recommendation of Andy and David: one language each year is more than enough. Seven in seven weeks is great to get a taste of each one and perhaps take a first jab at answering the above six questions, but you’ll never embark on the journey of learning (let alone mastering) the language: there’s simply too little time.
- 2019: C++11 and its ecosystem;
- 2020: ES6, keeping my Node/JS knowledge up to date, one
asyncat a time;
- 2021: Kotlin and Go;
- 2022: Elixir.
See you at ElixirConfEU in June!