During my recent coaching sessions I realized that different developers consider compilers in entirely different ways. In this post I’ll describe three dominant views I’ve encountered.
View 1: Dynamically typed aka what’s a compiler?
Type systems are complicated. It’s a good thing that we have dynamically typed languages, in which the type system is very forgiving and not very formalized. For beginners, it allows them to learn programming without having to spent a lot of time on complicated type theory, and advanced developers value the resulting expressivity and speed of development.
In terms of viewing a compiler though, I have found developers who have not yet ventured into the realm of strongly statically typed languages to sometimes make one of these two mistakes:
- Interpreters are like compilers of statically typed languages
- The compiler is your enemy
Interpreters are interpreting and executing source code one expression after the other. That’s why languages like Python or Ruby allow you to execute code all the way up to a line containing an error. The moment an erroneous line is reached, you do get some compiler error. That’s because interpreters are also a sort of compiler. The major difference to a statically typed language though, is that with dynamic typing, you have to execute code before you can detect type errors.
When a developer works with dynamic languages for a longer period before dabbling with a statically typed language, I sometimes see the second mistake. If you are used to dynamic typing and try to make your first steps in the land of static typing, the compiler feels like your enemy. What you could do in Python suddenly no longer works in Java or C#. That evil compiler thing starts getting into your way and it feels like a constant nagging that has to be satisfied via some arcane ritual.
Unfortunately, it isn’t until you dive deeper into type systems, before you come to realize that the compiler is not the enemy. It takes understanding, practice, and experience to reach the next view. Some developers, quite understandably, resign from static typing before reaching that point. If you are skeptical of static typing, make sure to also check out the third view below.
View 2: Statically typed aka the needy compiler
I’d guess the majority of developers shares this view. They realize the advantages of static type guarantees to some degree. Of course, there are disadvantages, because those fancy static type systems can get complicated and you need to learn how to satisfy this annoying compiler – just so that you can eventually run your program. Initially, this is a sort of frustration and with experience it just becomes a matter-of-factly acceptance that the needs of the compiler must be satisfied.
In languages like Java or C#, this is also the most advanced view one can reach for. As a consequence, in your daily work, you should actually aim to minimize the number of compiler errors you get. It’s kind of resembling an art form to refactor huge amounts of code whilst never getting into an uncompilable state.
But that’s also the downside of this view – the needy compiler is providing a kind of scaffolding that gives you some safety guarantees, but it’s not helping you. You as a developer have to take care of the compiler’s needs. Many discussions I had about dynamic vs static typing were limited due to a lack of knowledge of the next view – which is understandable, considering that most major languages fall into these first two views (Python, C++, C#, Java(script), Ruby to just name a few).
View 3: Strongly statically typed aka the compiler is your friend
If you are a software developer and haven’t yet investigated a language with a strong type system, I strongly urge you to do so. The type system and hence compiler of languages like Haskell, Idris or Elm are entirely different from the above views. Elm is particularly interesting here, since, these languages used to have a really steep learning curve, whereas Elm is catering very well for newcomers to these type systems. If you haven’t checked out any of these languages yet, I would suggest starting with Elm as it is the most approachable.
Why is it that stronger typing turns the compiler from a guarantee-providing scaffolding into a friend? Weak static typing adds type information to be able to provide you with guarantees. It is weak though, which means that there are certain holes in the type system, respectively the language, which severely limit the potential guarantees. While a
void * is a type, there is not much to guarantee about it. Similarly, a
String looks like a strong type, but if you can assign a
null value to it, you essentially blow a hole into your type system.
The above languages do not make such compromises on their type system and in return, they provide the compiler with guarantees that allow it to unleash its true potential and truly become your trusted friend. I may dive deeper into this in a different post, but for now, if this seems too vague for you, search the net for other blog posts, experiences or talks, about refactoring in Elm or smart code completion in Idris. These languages are no longer about satisfying the compiler, but instead, the compiler gently and ever so carefully guides you towards your goal.
PS: The dragon book cover used for this article suits the second view really well. Seems like the progress we made on programming languages may finally be able to tame that dragon.