A lot of questions get asked here about interpreted vs compiled language implements. I'm wondering whether the distinction actually makes any sense. (Actually the questions are usually about languages, but they are really thinking about the most popular implementations of those languages).

Today almost no implementation is strictly interpreted. i.e. pretty much nobody parses and runs the code one line at a time. Additionally, implementation which compile to machine code are also becoming less common. Increasingly, compilers target some sort of virtual machine.

In fact, most implementation are converging on the same basic strategy. The compiler produces bytecode which is interpreted or compiled to native code via a JIT. It is really a mix of the traditional ideas of compilation and interpretation.

Thus I ask: Is there a useful distinction between interpreted implementations and compiled implementation these days?

  • 7
    @DeadMG Not as new as you may think: A brief history of just-in-time... – yannis Feb 26 '12 at 19:06
  • 4
    @DeadMG Given that the majority of new languages introduced in the last 10 years or so primarily run on some kind of VM, I'd say he has a point. Of course there still are (and will be for decades to come) languages compiled to native code, and a JIT will remain luxury (or not, if the PyPy guys have their way). So yes, possible overstatement, but I agree that the mainstream (for now and the forseeable future) seems to be bytecode compiler + possibly JIT. – user7043 Feb 26 '12 at 19:08
  • 4
    @DeadMG, you must have a long white beard, if the VM model is "new" for you. P-code had been introduced in 1966 first. IBM Aix is around since 1986. – SK-logic Feb 27 '12 at 8:33
  • 6
    Things like unix shells, Tcl and alike would always be purely interpreted, so the distinction makes sense, at least in an academic CS. But it is true that when coders are mumbling about interpreters vs. compilers they're not making any sense in most of the cases. – SK-logic Feb 27 '12 at 8:35
  • 3
    @SK-logic, I think your comment is a better answer then any of the answers actually posted – Winston Ewert Feb 27 '12 at 17:53

It's important to remember that interpreting and compiling are not just alternatives to each other. In the end, any program that you write (including one compiled to machine code) gets interpreted. Interpreting code simply means taking a set of instructions and returning an answer.

Compiling, on the other hand, means converting a program in one language to another language. Usually it is assumed that when compilation takes place, the code is compiled to a "lower-level" language (eg. machine code, some kind of VM bytecode, etc.). This compiled code is still interpreted later on.

With regards to your question of whether there is a useful distinction between interpreted and compiled languages, my personal opinion is that everyone should have a basic understanding of what is happening to the code they write during interpretation. So, if their code is being JIT compiled, or bytecode-cached, etc., the programmer should at least have a basic understanding of what that means.

  • 3
    Yes the programmer should have a basic understanding. But I wonder if the compiled/interpreted terminology doesn't get in the way of that. – Winston Ewert Feb 26 '12 at 23:33
  • 2
    Thank you!! Interpreted is just a synonym for "executed", and that's how all programs are run. – gardenhead Oct 26 '16 at 14:53

The distinction is deeply meaningful because compiled languages restrict the semantics in ways that interpreted languages do not necessarily. Some interpretive techniques are very hard (practically impossible) to compile.

Interpreted code can do things like generate code at run time, and give that code visibility into lexical bindings of an existing scope. That's one example. Another is that interpreters can be extended with interpreted code which can control how code is evaluated. This is the basis for ancient Lisp "fexprs": functions that are called with unevaluated arguments and decide what to do with them (having full access to the necessary environment to walk the code and evaluate variables, etc). In compiled languages, you can't really use that technique; you use macros instead: functions that are called at compile time with unevaluated arguments, and translate the code rather than interpreting.

Some language implementations are built around these techniques; their authors reject compiling as being an important goal, and rather embrace this kind of flexibility.

Interpreting will always be useful as a technique for bootstrapping a compiler. For a concrete example, look at CLISP (a popular implementation of Common Lisp). CLISP has a compiler that is written in itself. When you build CLISP, that compiler is being interpreted during the early building steps. It is used to compile itself, and then once it is compiled, compiling is then done using the compiled compiler.

Without an interpreter kernel, you would need to bootstrap with some existing Lisp, like SBCL does.

With interpretation, you can develop a language from absolute scratch, starting with assembly language. Develop the basic I/O and core routines, then write an eval, still machine language. Once you have eval, write in the high level language; the machine code kernel does the evaluating. Use this facility to extend the library with many more routines and write a compiler also. Use the compiler to compile those routines and the compiler itself.

Interpretation: an important stepping stone in the path leading to compilation!

  • 1
    IMO, this is the best answer. I'm working on my own toy language and the last paragraph describes the way I'm developing it. It really makes working on new ideas much easier. Also +1 for mentioning CLISP bootstrap process. – sinan Jul 11 '12 at 7:07
  • In theory, any “interpreted” language can be made into a “compiled” one by generating an EXE file consisting of the interpreter plus the source code or bytecode for the interpreted program. Might not be very efficient, though. – dan04 Oct 26 '16 at 16:55
  • Read up on how Wirth et al invented P-code to simplify porting PASCAL to new machine architectures. That was in the early 1970s. – John R. Strohm Oct 26 '16 at 19:59
  • 1
    I suspect your opening paragraph is confusing compilation and interpretation with static and dynamic behavior, but I'll give you the benefit of the doubt and just ask for an example of a language with semantics that are "practically impossible" to compile. Regarding bootstrapping a compiler, it is true that the first implementation needs to be written in something other than the language you're implementing, but it does not have to be an interpreter, it could be a compiler written in another language. – 8bittree Aug 3 '17 at 22:38

Actually lots of implementations of languages are still strictly interpreted, you just may not be aware of them. To name a few: the UNIX shell languages, The Windows cmd and PowerScript shells, Perl, awk, sed, MATLAB, Mathematica and so on.

  • 3
    I believe Perl is internally compiled to bytecode, and at least Mathematica can be compiled. And nothing dictates the implementation of awk and sed (I believe some of the GNU coreutils compile regular expressions to finite automata before execution, which would arguably put them into the "compile to intermediate representation, interpret that" category). – user7043 Feb 26 '12 at 19:12
  • 1
    Actually I'm pretty sure that Perl, MATLAB, Mathematica all compile to bytecode. I'm not familiar with PowerScript, do you mean Powershell? If so, that using the CLR and so does use bytecode. – Winston Ewert Feb 26 '12 at 19:23
  • @WinstonEwert, sorry I did mean PowerShell. My understanding is that the translation into an intermediate form does not mean that something is not interpreted. Heck, the original Dartmouth BASIC interpreter translated the source into tokens before interpreting. Each of the tools I mentioned has a mode where it 1) reads a line of source, 2) translates that line into an executable form (possibly some intermediate code rather than native code), 3) executes the code for that line, 4) loop back to 1). That corresponds to my understanding of an interpreter. – Charles E. Grant Feb 26 '12 at 19:54
  • 2
    Bytecode does imply compiled. A bytecode compiler is simply a program that takes the source and converts it to bytecode. Hence all uses of bytecode must involve a bytecode compiler. But bytecode also has to be interpreted (or JITted). So anything using bytecode is an interpreter/compiler hybrid. – Winston Ewert Feb 27 '12 at 0:59
  • 4
    Really, my thing is that people toss out statements like "python is interpreted" and "Java is compiled" with no understanding of the implementations. I'm questing whether its even useful to describe an implementation in those terms. The truth usually more complicated, and trying to boil it down to interpreted/compiled isn't useful. – Winston Ewert Feb 27 '12 at 1:16

I think: Absolutely Yes.

In fact, most implementation are converging on the same basic strategy

Really, C++ aims to port to compiler domain some high level concept that's usually handed to interpreters, but it stays on the minority side...

  • 2
    Wait until Clang+LLVM become the most popular compiler toolchain. – SK-logic Feb 27 '12 at 8:38
  • @SK-logic, despite the name, I believe that Clang+LLVM produces native code. – Winston Ewert Feb 27 '12 at 15:15
  • 1
    @Winston Ewert, only if you want to. You can stop on LLVM IR level and do whatever you want with it then - interpret it, JIT-compile it, instrument it any way you like. You can even translate it to Javascript and then pass through an interpreter: github.com/kripken/emscripten/wiki – SK-logic Feb 27 '12 at 15:20
  • @SK-logic, neat stuff! Didn't know LLVM could do that. – Winston Ewert Feb 27 '12 at 15:25
  • 1
    The beauty of llvm is this deliberate separation between the front end and back end. And the tools for manipulating the middle before targeting the instruction set. You can merge your entire project into bytecode and then optimize across the whole thing, with other compilers you would need to have a single file source or at least one that includes its way down through the source tree so the compiler acts on one combined source. Also one set of tools under llvm is generic to all targets, you dont have to build for each target, one compiler fits all (at least to the asm of the target). – old_timer Feb 28 '12 at 7:22

Useful distinction: interpreted programs can modify themselves by adding or changing functions at runtime.

  • 8
    Nonsense. Self-modifying (machine) code is the oldest trick in the book. Then again, some argue even native code is ultimately interpreted by an interpreter cast into silicon (the CPU). But if we assume that, all code is interpreted and there's no distinction to make. – user7043 Feb 26 '12 at 19:09
  • 2
    @delnan is right. I'll just add that modern languages can modify themselves by creating new classes dynamically and loading/unloading libraries (or "assemblies" in .NET for example) – Jalayn Feb 27 '12 at 7:35
  • 5
    Common Lisp is compiled, but you still can replace function definitions in runtime easily. – SK-logic Feb 27 '12 at 8:37
  • That's a really interesting and necessary (for instance in Prolog) feature of interpretation. – CapelliC Nov 18 '12 at 11:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.