A programming language is a bridge between the human (the programmer), and the machine that will execute the program.
As a result, a program always has two representations, and it is the responsability of the language to provide a well defined and efficient transition mechanism between the two:
•
The one close to human, that I will call source, generaly a text that is just a compact representation of a tree
•
The one close to the machine, that I will call machine code, a set of flat instructions and functions call
The first one exists because when developping complex projects, a respresentation to close to the machine codes is not suited for the humans way of thinking, we call it too low level, so wastes the programmer mental capabilities.
The second one exists because the computer processors set of features are the same on all of them and have been so for decades. The processors design improvements over time has only changed execution speed.
On many languages, the language tries to be 'smart', that is define a set of new cool features that will make it 'unique' 'better' than others. This is just stupid and expressing the unability of the so called new language designer to understand the languages technology as old as 1959. In 1959, Lisp was published and showed once for all that the best way to provide all cool features at once is just to have the language be abble to rewrite the source code. Since then, preprocessor macros and templates should just be seen as toy tools since they are not even Turing machine equivalent (1).
Also an impressing achievement, Lisp was not yet adressing two more issues.
•
First, when rewriting, the expressions are not typed, so mixing apple and oranges is possible leading to debugging beeing harder because the language is not warning in case of some obviously wrong code a typed rewriting system would have noticed automaticaly.
•
Second, it did not care about execution speed, so does not let the program rewrite the machine code.
Language
Creation year
Source rewritting
Turing equivalent source code rewriting
Typed source rewriting
Machine code rewriting
Turing equivalent machine code rewriting
Lisp
1958
Meta functions
✔
✘
None
✘
Basic
1963
Interpreted 'eval' instruction (2)
✔✘
✘
None
✘
C
1972
Preprocessor macros
✘
✘
None
✘
C++
1983
Preprocessor macros Templates
✘
✘
None
✘
Perl
1987
None
✘
Python
1990
None
✘
PHP
1994
Interpreted 'eval' instruction (2)
✔✘
✘
None
✘
Java
1995
Generic
✘
✘
None
✘
Javascript
1995
Interpreted 'eval' instruction (2)
✔✘
✘
None
✘
Pliant
1999
Meta functions
✔
✔
Optimizers
✔
(1)
Beeing Turing machine equivalent means that the language could theorically be used to compute anything. It might seem impressive, but in facts any programming language, even very limited is Turing machine equivalent.
Having an 'eval' instruction is the powerfull trick that is easy to provide on interpreted (slow) languages, but even if it's turing machine equivalent on the theorical side, you can't practicaly use it to generate any significant code.