According to their definitions, the difference between a compiler and an interpreter seems clear enough:
- interpreter a program that directly executes instructions written in a programming language
- compiler a program that transforms source code in a low(er)-level language
If you dig deeper, though, you might find some blurring between the two.
In fact an interpreter could translate the source language in a intermediate form, to speed up execution. That is what usually happens with a language that relies on a virtual machine. This naturally lead to the some questions:
Are all languages that use a virtual machine interpreted?
Are they all actually compiled?
You might say both: a language is first compiled in an intermediate form/language and then this intermediate thing is interpreted at run time. Which also lead to another issue, a compiler and an interpreter should not be thought as one program, but more of a group of programs, a system. What you, as a user, think as a compiler may actually include more than one program. For instance, it may include a linker: a program that combines different object files in one file, so that it can be more easily used. Something similar could be said of an interpreter.
Can You Tell Me Everything About Compilers & Interpreters?
So, which are all the pieces that compose a compiler or an interpreter? You could look for a precise and technical answer to such questions in academia. Or you can find discussions on these issues on StackOverflow.
What it really matters to us as developers, or even us as creators of a language, is what are the differences in working with them. Both have advantages and disadvantages, and in fact some languages can have both an interpreter and a compiler, or more than one. That is what we are going to see.
The main point still stands: an interpreter executes the code now, a compiler prepares the source code for an execution that comes later. All the practical differences descend from these different objectives.
How Do You Distribute Programs
In practical terms one important difference is that a compiler generates a stand-alone program, while an interpreted program always need the interpreter to run.
Once you have a compiled program you can run it without needing to install anything else. This simplifies distribution. On the other hand the executable work on one specific platform: different operating systems and different processors need different compiled versions.
If you are going to interpret a program you can distribute the same copy to users on different platforms. However they will need an interpreter that runs on their specific platform. You could either distribute the original source code or an intermediate form. An intuitive way to look at an interpreter is this: it is like the
This is a technical difference that leads to important real consequences: it is easier to make cross-platform programs with an interpreted programming language.
That is because, for the most part, you are just creating a program for the interpreter platform. It will be the interpreter itself that will translate it into the proper form for the real platform (e.g., Windows/Linux and x86/ARM). Of course, there are still some differences in each platform of which you must be aware. A common example is the directory separator character.
When you compile a program, instead, you need to take care yourself of all the little differences between each platform. This happens in part because compiled languages tend to be low(er) level languages, such as C++, so they give you lower access to the system and thus more responsibility. But another reason is that all the libraries you are using need themselves to support different platforms. So if they do not support Windows, you cannot support Windows.
Speed Has Multiple Faces
Again, in the case of speed, we have a sort of paradox: a compiler is both faster and slower than an interpreter. Many people knows that a compiled program is much faster than an interpreted one, but this is not the whole picture. A compiled program is faster to run that an interpreted program, but it takes more time to compile and run a program than to just interpret it.
A compiler indeed produces faster programs. It happens fundamentally because it must analyze each statement just once, while an interpreter must analyze it each time. Furthermore, a compiler can optimize the executable code it produces. That is both because it knows exactly where it will run and also it takes time to optimize the code. Time that would make the interpretation too slow.
Runtime Speed Versus Development Speed
You might think that this is nitpicking: if you compile a program it runs faster, the time that it takes to compile does not matter. This is usually the old school opinion. And without doubt the resulting program is run more times than it is compiled. So who cares if the development takes more time? Well, for sure it takes a “bring the pain” attitude to development that is somewhat admirable. But what if the gains in run time are not relevant, while the losses in development productivity are significant?
It is one thing if you creating an operating system and another one if you making a selfie app. Even your users might prefer a not-even-noticeable loss in run time speed in exchange for getting features quicker. There is not a one-size-fits-all answer: in some contexts productivity matters more than speed, in others the inverse is true.
The Mysteries Of Debugging
There is another particular aspect that ends up being more uncertain than what one would aspect: debugging. On paper debugging is easier while using a interpreter than using a compiler. That is true for several reasons:
- with the interpreter there is one version of the executable; you don’t need a debug version for development and a release one for the end user
- there are fewer platform specific bugs using an interpreter
- since the interpreter transforms code on the fly, the information from the source code is still available
- since the interpreter executes one statement at a time it is easier to find a mistake
The Difference That Development Tools Makes
Having said that, if we put them in the same context, each one with a great IDE and supporting tools, the situation comes back to normal. Indeed many interpreted environment are used by people that want to learn how to use a new language. It is easier to test and find what is right and wrong by looking at what happens line by line and in real time.
We have seen the main differences that matters between a compiler and an interpreter. More importantly we have seen that the consequences of different philosophies are more important than the technical ones. In short, there are cultures that come with certain technical choices that end up being relevant on their own. If you want speed and ease of development you are going to pick all the technologies for speed and not just one. And your users are going to follow your lead.
This is a crucial aspect to think through, especially if you want to create your own programming language. Rasmus Lerdorf created PHP to be easy to use. And indeed it was incredibly easier to use compared to the alternatives, at least at the time of its creation. But it started more as a library than as a language. And while it has improved a lot, it still suffers from its beginnings. You can still create good PHP code, but fewer of its users than usual do. Because if you just need something that works, security, maintenance, etc., all the rest come later.
If you want to know how you can practically build an interpreter or a compiler for your language, you may want to take a look at the resources to create a programming language. If you want to learn that, and all that you need to create your own language, you just need to pick a great book, loved by kids and adults alike, on how to create pragmatic, lightweight languages.
5 things to get right when building a language
Receive the checklist by email and get more tips on building languages