What is a programmable programming language?
Take a "regular" programming language like Javascript. You define a function like so:
let add = (a, b) => a + b
You can then call the function with numbers as arguments:
You can also call the function with more complex arguments:
add(700 * 3 * 2, Math.pow(2, 16)) // Returns 69736
Importantly, the add
function has no knowledge of the fact that you performed all those multiplications to compute the first argument, and it certainly doesn’t know that you called Math.pow
to compute the second.
The Javascript runtime evaluates these two arguments before invoking add
, so that by the time add
is called, the call looks something like this:
In a language like Javascript, you can’t write this function in a way that allows add
to inspect the operations that went into calculating its arguments.
In a Lisp dialect, though? It’s easy! You can write a special kind of function called a macro that receives all it’s arguments as-is. The macro can inspect the whole thing before deciding what to do, making some truly dynamic behavior possible.
Here’s the same add
function in Clojure, a Lisp dialect:
(defn add [a b]
(+ a b))
(add (* 700 3 2) (Math/pow 2 16)) ; Returns 69736
So far this is the same as the Javascript version. The add
function has no idea that you multiplied a bunch of numbers together to create its first argument, ditto for Math/pow
.
Let’s create an add
macro instead. To keep things simple, let’s have it print its arguments and return nothing:
(defmacro add [a b]
(println "arg a is" a "of type" (type a))
(println "arg b is" b "of type" (type b)))
And here’s what you’d see if you invoked it:
formulate=> (add (* 700 3 2) (Math/pow 2 16))
arg a is (* 700 3 2) of type clojure.lang.PersistentList
arg b is (Math/pow 2 16) of type clojure.lang.PersistentList
The add
function is able to "see" its arguments in their entirety! Note that clojure.lang.PersistentList
is simply the type Clojure uses for its linked list data structure, so these arguments can be manipulated just like regular lists.
Crucially, you can now have the add
macro generate an implementation based on these arguments. You could run Math/abs
on every single integer that was used to construct the arguments to add
, or attempt to detect if one of the arguments has overflowed.
This was a very contrived example, but it goes to show how a programmable programming language can let you do things that are impossible in other languages.