The Dichotomy of Programming Languages: Compiled vs. Interpreted and the Blurring Lines
Programming languages are typically classified as either compiled or interpreted. This categorization is usually based on the most popular or widespread implementations of a language. For instance, BASIC is often called an interpreted language, and C a compiled one, despite the existence of BASIC compilers and C interpreters. However, in practice, there is rarely anything about a language that requires it to be exclusively compiled or exclusively interpreted.
An interpreted language is one in which the source code is directly executed, without being compiled first. In an interpreted language, an interpreter reads the source code and executes it directly, one line at a time. This means that changes to the source code can be immediately seen in the output. However, interpretation is generally slower than compilation because the interpreter has to translate each line of code at runtime.
A compiled language is one in which the source code is compiled into machine code before it is executed. In a compiled language, a compiler reads the source code and generates an executable file that can be run directly. This means that the code runs faster than interpreted code, but any changes to the source code require recompilation.
In practice, many programming languages can be either compiled or interpreted. The choice of whether to use a compiler or interpreter depends on various factors, such as the nature of the application, the desired speed of execution, and the available resources. Some languages have specifications that require implementations to include a compilation facility. For example, Common Lisp requires implementations to include a compiler, although there is nothing inherent in the definition of Common Lisp that prevents it from being interpreted.
Some languages have features that are very easy to implement in an interpreter but make writing a compiler much harder. For example, APL, SNOBOL4, and many scripting languages allow programs to construct arbitrary source code at runtime with regular string operations and then execute that code by passing it to a special evaluation function. To implement these features in a compiled language, programs must usually be shipped with a runtime library that includes a version of the compiler itself.
Interpretation does not replace compilation completely. It only hides it from the user and makes it gradual. Even though an interpreter can itself be interpreted, a set of directly executed machine instructions is needed somewhere at the bottom of the execution stack. Furthermore, for optimization, compilers can contain interpreter functionality, and interpreters may include ahead-of-time compilation techniques. For example, an expression can be executed during compilation, and the results can be inserted into the output program, thus preventing it from having to be recalculated each time the program runs, which can greatly speed up the final program.
Modern trends towards just-in-time compilation and bytecode interpretation at times blur the traditional categorizations of compilers and interpreters even further. Just-in-time compilation is a technique used by some virtual machines to improve the performance of interpreted code. The virtual machine compiles frequently executed parts of the code into machine code at runtime, allowing it to run faster. Bytecode interpretation is another technique used by some languages to improve performance. Bytecode is an intermediate code that can be interpreted more efficiently than the original source code, and it can be compiled into machine code for even better performance.
In conclusion, the categorization of programming languages as either compiled or interpreted is based on the most popular or widespread implementations of a language. However, in practice, many languages can be either compiled or interpreted. The choice of whether to use a compiler or interpreter depends on various factors, such as the nature of the application and the desired speed of execution. Modern trends towards just-in-time compilation and bytecode interpretation at times blur the traditional categorizations of compilers and interpreters even further.