15

As per Wikipedia: A compiled language is a programming language whose implementations are typically compilers (translators that generate machine code from source code). And an interpreted language is a type of programming language for which most of its implementations execute instructions directly and freely, without previously compiling a program into machine-language instructions.

Hence the following is clear.

  • C, C++ and few other similar languages - Compiled Language
  • Shell script, Perl, Ruby and some more - Interpreted Language

However, there is a 3rd kind of language as well. Languages like C# and Java which use both a compiler and a JIT while running. So my question is, is there a separate name for such languages or they can be categorized to either of the above types? An explanatory answer would be more helpful?

EDIT:

From Wikipedia and this SO post: Interpreted vs Compiled it is evident that there are 2 well-defined language implementation types. But my question is about the fact that is it sufficient to have 2 categories, can all languages be fit within these 2 or is there a 3rd one?

12
  • 7
    The terms "compiler" and "interpreter" have become a bit blurry over the years. Any program that turns one language into another is a compiler, including things like Typescript, which have their own special name: transpilers. I think you're reasonably safe sticking with the Wikipedia descriptions. Commented Oct 4, 2019 at 19:30
  • 19
    Also, languages themselves are neither compiled nor interpreted. It's the implementations of those languages that are compiled or interpreted. You can write a compiler or interpreter for any programming language. Commented Oct 4, 2019 at 19:38
  • 4
    "However as there are these categories" – You are missing the point Robert is making. These categories don't exist. They are non-sensical. You cannot talk about an "interpreted language" because there is no such thing. The language isn't interpreted, the implementation is. Interpretation and compilation are traits of the interpreter or compiler, not the language. The language is just the language. Commented Oct 4, 2019 at 19:45
  • 1
    @TheodorosChatzigiannakis Imprecise, lacking detail, not covering all cases, vague. But not nonsensical, it does have a meaning. Commented Oct 5, 2019 at 13:31
  • 1
    @JoseAntonioDuraOlmos The question "is a language compiled" is like the question "is the number 7 unemployed". I'm having trouble talking about the level of precision or detail in such a statement, because fundamentally it ascribes a quality to a thing on which that quality is non-applicable. Commented Oct 5, 2019 at 13:47

5 Answers 5

51

The answer to your question:

Can every language be categorized as either compiled or interpreted?

Is "No", but not for the reason you think it is. The reason is not that there is a third missing category, the reason is that the categorization itself is nonsensical.

There is no such thing as a "compiled language" or an "interpreted language". Those terms are not even wrong, they are nonsensical.

Programming languages are sets of abstract mathematical rules, definitions, and restrictions. Programming languages aren't compiled or interpreted. Programming languages just are. [Credit goes to Shriram Krishnamurthi who said this in an interview on Channel 9 years ago (at about 51:37-52:20).]

In fact, a programming language can perfectly exist without having any interpreter or compiler! For example, Konrad Zuse's Plankalkül which he designed in the 1930s was never implemented during his lifetime. You could still write programs in it, you could analyze those programs, reason about them, prove properties about them … you just couldn't execute them. (Well, actually, even that is wrong: you can of course run them in your head or with pen and paper.)

Compilation and interpretation are traits of the compiler or interpreter (duh!), not the programming language. Compilation and interpretation live on a different level of abstraction than programming languages: a programming language is an abstract concept, a specification, a piece of paper. A compiler or interpreter is a concrete piece of software (or hardware) that implements that specification. If English were a typed language, the terms "compiled language" and "interpreted language" would be type errors. [Again, credit to Shriram Krishnamurthi.]

Every programming language can be implemented by a compiler. Every programming language can be implemented by an interpreter. Many modern mainstream programming languages have both interpreted and compiled implementations. Many modern mainstream high-performance programming language implementations have both compilers and interpreters.

There are interpreters for C and for C++. On the other hand, every single current major mainstream implementation of ECMAScript, PHP, Python, Ruby, and Lua has a compiler. The original version of Google's V8 ECMAScript engine was a pure native machine code compiler. (They went through several different designs, and the current version does have an interpreter, but for many years, it didn't have one.) XRuby and Ruby.NET were purely compiled Ruby implementations. IronRuby started out as a purely compiled Ruby implementation, then added an interpreter later in order to improve performance. Opal is a purely compiled Ruby implementation.

Some people might say that the terms "interpreted language" or "compiled language" make sense to apply to programming languages that can only be implemented by an interpreter or by a compiler. But, no such programming language exists. Every programming language can be implemented by an interpreter and by a compiler.

For example, you can automatically and mechanically derive a compiler from an interpreter using the Second Futamura Projection. It was first described by Prof. Yoshihiko Futamura in his 1971 paper Partial Evaluation of Computation Process – An approach to a Compiler-Compiler (Japanese), an English version of which was republished 28 years later. It uses Partial Evaluation, by partially evaluating the partial evaluator itself with respect to the interpreter, thus yielding a compiler.

But even without such complex highly-academic transformations, you can create something that is functionally indistinguishable from compilation in a much simpler way: just bundle together the interpreter with the program to be interpreted into a single executable.

Another possibility is the idea of a "meta-JIT". (This is related in spirit to the Futamura Projections.) This is e.g. used in the RPython framework for implementing programming languages. In RPython, you write an interpreter for your language, and then the RPython framework will JIT-compile your interpreter while it is interpreting the program, thus producing a specialized compiled version of the interpreter which can only interpret that one single program – which is again indistinguishable from compiling that program. So, in some sense, RPython dynamically generates JIT compilers from interpreters.

The other way around, you can wrap a compiler into a wrapper that first compiles the program and then directly executes it, making this wrapped compiler indistinguishable from an interpreter. This is, in fact, how the Scala REPL, the C♯ REPL (both in Mono and .NET), the Clojure REPL, the interactive GHC REPL, and many other REPLs are implemented. They simply take one line / one statement / one expression, compile it, immediately run it, and print the result. This mode of interacting with the compiler is so indistinguishable from an interpreter, that some people actually use the existence of a REPL for the programming language as the defining characteristic of what it means to be an "interpreted programming language".

Note, however, that you can't run a program without an interpreter. A compiler simply translates a program from one language to another. But that's it. Now you have the same program, just in a different language. The only way to actually get a result of the program is to interpret it. Sometimes, the language is an extremely simple binary machine language, and the interpreter is actually hard-coded in silicone (and we call it a "CPU"), but that's still interpretation.

Some people say that you can call a programming language "interpreted" if the majority of its implementations are interpreters. Well, let's just look at a very popular programming language: ECMAScript. There are a number of production-ready, widely-used, high-performance mainstream implementations of ECMAScript, and every single one of them includes at least one compiler, some even multiple compilers. So, according to this definition, ECMAScript is clearly a compiled language.

You might also be interested in this answer of mine, which explains the differences and the different means of combining interpreters, JIT compilers and AOT compilers and this answer dealing with the differences between an AOT compiler and a JIT compiler.

It is possible to categorize language implementations to some degree. In general, we have the distinction between

  • compilers and
  • interpreters (if the interpreter interprets a language that is not meant for humans, it is also often called a virtual machine)

Within the group of compilers, we have the temporal distinction when the compiler is run:

  • Just-In-Time compilers run while the program is executing
  • Ahead-Of-Time compilers run before the program starts

And then we have implementations which combine interpreters and compilers, or combine multiple compilers, or (much more rare) multiple interpreters. Some typical combinations are

  • mixed-mode execution engines which combine an interpreter and a JIT compiler that both process the same program at the same time (examples: Oracle HotSpot JVM, IBM J9 JVM)
  • multi-phase [I invented that term; I don't know of a widely-used one] execution engines, where the first phase is a compiler that compiles the program to a language more suitable for the next phase, and then a second phase which processes that language. (There could be more phases, but two is typical.) As you can probably guess, the second phase can again use different implementation strategies:
    • an interpreter: this is a typical implementation strategy. Often, the language that is interpreted is some form of bytecode that is optimized for "interpretability". Examples: CPython, YARV (pre-2.6), Zend Engine
    • a compiler, which makes this a combination of two compilers. Typically, the first compiler translates the language into some form of bytecode that is optimized for "compilability" and the second compiler is an optimizing compiler that is specific to the target platform
    • a mixed-mode VM. Examples: YARV post-2.6, Rubinius, SpiderMonkey, SquirrelFish Extreme, Chakra

But, there are still others. Some implementations use two compilers instead of a compiler and an interpreter to get the same benefits as a mixed-mode engines (e.g. the first few years, V8 worked this way).

RPython combines a bytecode interpreter and a JIT, but the JIT does not compile the user program, it compiles the bytecode interpreter while it interprets the user program! The reason for this is that RPython is a framework for implementing languages, and in this way, a language implementor only has to write the bytecode interpreter and gets a JIT for free. (The most well-known user of RPython is of course PyPy.)

The Truffle framework interprets a language-agnostic AST, but at the same time it specializes itself to the specific AST, which is kind-of like compilation but also kind-of not. The end result is that Truffle can execute the code extremely fast, without knowing too much about the language-specifics. (Truffle is also a generic framework for language implementations.) Because the AST is language-agnostic, you can mix and match multiple languages in the same program, and Truffle is able to perform optimizations across languages, such as inlining a Ruby method into an ECMAScript function etc.

Macros and eval are sometimes cited as features that cannot possibly be compiled. But that is wrong. There are two simple ways of compiling macros and eval. (Note that for the purpose of compilation, macros and eval are somewhat dual to each other, and can be handled using similar means.)

  1. Using an interpreter: for macros, you embed an interpreter into the compiler. For eval, you embed an interpreter into the compiled program or into the runtime support libraries.
  2. Using a compiler: for macros, you compile the macro first, then embed the compiled macro into your compiler and compile the program using this "extended" compiler. For eval, you embed a compiler into the compiled program or into the runtime support libraries.
15
  • 5
    I like this. But, by the Wikipedia definition as stated in the OP, the practical distinctions we generally care about are A) whether I can deploy the code I wrote directly to the target machines and B) whether I need to ship a general interpreter to the target runtime machines. ... And those lines may also be a bit fuzzy. But, in addition to the academic sort of analysis, there are four distinct practical categories here that determine development and deployment workflows.
    – svidgen
    Commented Oct 4, 2019 at 22:27
  • 6
    "... you can of course run them in your head or with pen and paper" That would make you the interpreter
    – 12Me21
    Commented Oct 5, 2019 at 3:37
  • 3
    @svidgen: But this distinction has nothing to do with the language. Whether or not you need to ship an interpreter to the target machine depends on whether or not the target machine has an interpreter for that language installed. For example, for ECMAScript, I don't need to ship an interpreter to the target machine because every version of Microsoft since Windows ME, every version of macOS since … I don't know exactly, but a loooong time, and pretty much every desktop version of a Linux distribution ships with an ECMAScript interpreter pre-installed. Does that make ES a compiled language? Commented Oct 5, 2019 at 6:42
  • 2
    @JörgWMittag "But this distinction has nothing to do with the language". I has a LOT to do with the language in a Software Engineering setting. The tools available for a language are a major factor impacting the choice of language. It is not such a factor in other settings, but this is Software Engineering. Commented Oct 5, 2019 at 12:00
  • 1
    @JörgWMittag, I'm not really convinced, TBH. If you want to get misty-eyed and philosophical about it, as is the angle your answer takes, "a language" is not an isolated, tangible thing with clear boundaries at all. The entire "cultural context" and "normative usage" of the language is an essential aspect of the language. ... And, in the English Language, when we talk about whether a language is "compiled" or "interpreted", we're generally not concerned with misty-eyed philosophical correctness. We're generally concerns with how the language is used. ... Know what I mean?
    – svidgen
    Commented Oct 5, 2019 at 17:47
13

If we are being pedantic, there is no such thing as a compiled or interpreted language, since any language could be in principle be implemented either by a compiler or an interpreter. However, most languages follow a relatively consistent implementation strategy. C++ is almost always compiled to native code. Python is almost always run via a bytecode interpretor. Java is almost always run via a JIT comiler. So, if we don't insist on an obtuse pedanticness, it does make sense to talk about compiled or interpreted languages.

However, language implementation strategy does not neatly fit into the compiled/interpreted dichotomy. Essentially no languages are strictly interpreted, executed directly from the source. This would be very slow. Instead, virtually all "interpreted" language implementations compile the source into something (often bytecode) which can be more effeciently executed. On top of this, some implementations JIT compile that bytecode into native code at run time. Even languages that we think of as being compiled often contain some amount of interpretation. For example, printf in C is effectively an interpreter of the format string.

So, I would argue that it doesn't make sense to try and categorize languages into compiled or interpreted. Pretty much any languages is some degree of hybrid of the two approaches. (And yes, if we are pedantic, it is language implementations not languages which are compiled/interpreted).

3
  • 2
    OP is giving a definition for compiled language. "A compiled language is a programming language whose implementations are typically compilers". Since you yourself claim that "C++ is almost always compiled to native code" that means that C++ is indeed a compiled language under OP's definition and thus there exists such a thing as a compiled language (again, under OP's definition). We should strive to answer OP's question in OP's setting. Commented Oct 5, 2019 at 13:22
  • @JoseAntonioDuraOlmos, that's what I said. Its prefectly fine to talk about compiled lanuages as languages which are usually compiled. However, languages run a complex gamut of combinations of compilation and interpretation, so trying to force languages into such a binary classification doesn't make sense. Commented Oct 5, 2019 at 16:24
  • @JoseAntonioDuraOlmos: However, the OP also defines "an interpreted language is a type of programming language for which most of its implementations execute instructions directly and freely, without previously compiling a program into machine-language instructions" and then goes on to list Ruby as an example of an interpreted language, despite the fact that all currently existing implementations of Ruby never interpret Ruby and always compile it to some machine language. So, the OP doesn't even follow their own definition. Commented Oct 5, 2019 at 19:08
1

No, the classification compiled vs. interpreted is not relevant for languages.

Compilers and interpreters are only means to deliver a language. And technology behind these means evolves. Practical examples:

  • In the late 80's Instant C from Rational Systems was a C interpreter (yes!). It was quite impressive although too slow for performance sensitive development.
  • For a long time, BASIC was a typically interpreted language. But all of the sudden, a market of BASIC compilers emerged (e.g. Visual Basic).
  • UCSD pascal, the first language delivered with a virtual machine was considered by many as a compiler, because the source code was compiled into a a bytecode, the p-code. But some saw it in the interpreter category since the p-code was in reality not compiled into machine code but itself interpreted, with a consequence of a slower performance.
  • Transpilers were and are still used to convert one language into another, possibly to a compiled language, using some additional libraries to cope with the more dynamic language constructs.
  • Virtual machine combined with JIT-compiler technology (e.g. JVM, CLI/CLR) allows nowadays to compile a lot of initially interpreted language (examples: IronPython for Ptyhton, or Clojure as a Lisp dialect).

Finally an interesting article of how the same language can evolve from interpreted to compiled.

0

Some versions of Basic tokenize the statements in a source code file, something between interpretive and compiled.

APL (a programming language), has an execute operator, that takes a variable string as a parameter, and then interprets and executes it. There's a create function operator, that takes a variable string matrix and creates a function from it. This would require some type of run time compilation in order to consider the language as compiled.

0

Under OP's definition of compiled language and interpreted language.

Other categories would be:

  • None. A language for which there exist neither interpreters nor compilers. This is the case of many languages in academic computer science.
  • Mixed. A language for which most of its implementations partially compile it and partially execute it. As OP mentions this is the case for Java. And no, I don't know a popular name for this category. JIT compiled languages is what I most often hear them referred to.

A language may change categories as new tools are developed or use trends change.

6
  • 1
    I find your distinctions confusing. Why is Java interpreted because there exist interpreted JVMs? JVMs don't interpret Java, they interpret JVML, which is a completely different language. Why is Java compiled because there exist Java processors? Java processors don't execute Java, they execute JVML, which is a completely different language. (Which makes "Java processor" a really confusing name.) Why is C++ compiled when there exist C++ interpreters? Commented Oct 5, 2019 at 11:27
  • "Of course, as people develop new tools, a language which previously was not a compiled/interpreted/mixed language may become one." – That seems strange. It means that a language can become something different without any change to the language whatsoever. How can a language change when it doesn't change? Commented Oct 5, 2019 at 11:32
  • 1
    According to your definition, is Python an interpreted language? Also, you say that Java is an interpreted language because I can translate it to JVML and then interpret that with a JVM. By the same logic, x86 assembly is an interpreted language because I can translate it to native x86 machine code and interpret that with an Intel CPU. Commented Oct 5, 2019 at 12:05
  • In fact, by that logic, every language that can be executed is interpreted, since compilation only translates from one language to another. Execution requires an interpreter, without an interpreter, a program can never run. Commented Oct 5, 2019 at 12:24
  • "What does the phrase "interpreted language" mean to you?" – It means nothing to me. It is literally meaning-less. Interpretation is a trait of an implementation for a language, it is not a trait of the language. "To me and to most people I've talked with it means "a language for which there exists an interpreter"" – I have my doubt that most people will agree with you that C and C++ are interpreted languages, which they are, according to your definition. Commented Oct 5, 2019 at 12:33

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