There are lots of languages that allow some kind of metaprogramming.
In particular, I am surprised to see no answer talking about the Lisp family of languages.
From wikipedia :
Metaprogramming is the writing of computer programs with the ability
to treat programs as their data.
Later in the text :
Lisp is probably the quintessential language with metaprogramming
facilities, both because of its historical precedence and because of
the simplicity and power of its metaprogramming.
Lisp languages
A quick made up intro to Lisp follows.
One way to see code is as a suite of instructions : do this, then do that, then do this other thing... This is a list ! A list of things for the program to do. And of course you can have lists inside lists to represent loops and so on..
If we represent a list containing the elements a, b, c, d like this : (a b c d) we get something that looks like a Lisp function call, where a
is the function, and b
, c
, d
are the arguments.
If fact the typical "Hello World!" program could be written like so : (println "Hello World!")
Of course b
, c
or d
could be lists that evaluate to something as well. The following : (println "I can add :" (+ 1 3) )
would then print ""I can add : 4".
So, a program is a serie of nested lists, and the first element is a function. The good news is that we can manipulate lists ! So we can manipulate programming languages.
The Lisp advantage
Lisps are not so much programming languages as much as a toolkit for making programming languages. A programmable programming language.
This is not only much easier in Lisps to make new operators, it is also nearly impossible to write some operators in other languages because arguments are evaluated when passed to the function.
For instance in a C-like language let's say that you want to write a if
operator yourself, something like :
my-if(condition, if-true, if-false)
my-if(false, print("I should not be printed"), print("I should be printed"))
In this case both arguments will be evaluated and printed, in an order dependant of the order of the evaluation of the arguments.
In Lisps, writing an operator (we call it a macro) and writing a function is about the same thing and used in the same way. The major difference being that parameters to a macro are not evaluated before being passed as arguments to the macro. This is essential to be able to write some operators, like the if
above.
Real-world languages
Showing how exactly is a bit out of scope here, but I encourage you to try programming in one Lisp to learn more. For instance you could have a look at :
- Scheme, an old, quite "pure" Lisp with a small core
- Common Lisp, a bigger Lisp with a well integrated object system, and many implementations (it is ANSI-standardized)
- Racket a typed Lisp
- Clojure my favorite, the examples above were Clojure code. A modern Lisp running on the JVM. There are some examples of Clojure macros on SO as well (but this is not the right place to start. I would look at 4clojure, braveclojure or clojure koans at first)).
Oh and by the way, Lisp means LISt Processing.
Regarding your examples
I am going to give examples using Clojure below :
If you can write an add
function in Clojure (defn add [a b] ...your-implementation-here... )
, you can name it +
like so (defn + [a b] ...your-implementation-here... )
. This is in fact what is done in the real implementation (the body of the function is a bit more involved but the definition is essentially the same as I wrote above).
What about infix notation ? Well Clojure uses a prefix
(or Polish) notation, so we could make an infix-to-prefix
macro that would turn prefixed code into Clojure code.
Which is actually surprisingly easy (it is actually one of the macro exercises in the clojure koans) ! It can also be seen in the wild, for instance see Incanter $=
macro.
Here is the simplest version from the koans explained :
(defmacro infix [form]
(list (second form) (first form) (nth form 2)))
;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result
To drive the point even further, some Lisp quotes :
“Part of what makes Lisp distinctive is that it is designed to evolve.
You can use Lisp to define new Lisp operators. As new abstractions
become popular (object-oriented programming, for example), it always
turns out to be easy to implement them in Lisp. Like DNA, such a
language does not go out of style.”
— Paul Graham, ANSI Common Lisp
“Programming in Lisp is like playing with the primordial forces of the
universe. It feels like lightning between your fingertips. No other
language even feels close.”
— Glenn Ehrlich, Road to Lisp
(+)
isNum a => a -> a -> a
IIRC. you can also define functions so they can be written infixed (a + b
instead of(+) a b
)