A hot take on Lisp metaprogramming, Autology
is a Lisp with access
to its own interpreter.
Autology is a functional interpreted Lisp language, written in Clojure.
The Autology interpreter function is defined as a data structure which is available to the Autology program, it is bound to the variable *i*
.
Whenever an expression needs to be evaluated, Autology will retrieve the current value of *i*
from the current lexically scoped execution environment and use it to evaluate the expression.
By binding new values of *i*
we can modify the behaviour of the Autology language while our program is running.
Mainly for fun and to explore a neat idea.
Autology is not a particularly useful language as-is, its very slow and resource intensive.
Macros are the normal tool for Lisp languages to modify their syntax or behaviour, but these are normally only available at compile time. Autology allows dynamic rewriting of the language at runtime.
Even other non-Lisp languages are able to define some form of Domain Specific Language to allow programmers to express problems more clearly, however these are generally quite restrictive in scope. Autology allows full ad-hoc re-writing of the syntax of the language, as well as defining new features or removing existing ones.
Well to start with you might like to add functions to the language. Autology doesn't have them by default, but we can rebind the *i*
symbol to a data structure representing a new interpreter function, one that does have functions.
(bind (;; grab a copy of the `:atl/eval-list` section of the
;; interpreter which is responsible for evaluating lists.
original (get-marker *i* :atl/eval-list)
;; define a case test+body for use when the list expression
;; starts with our function special form, in this case `λ`.
λ-form (qu (λ (let [[_λ params body] e]
(fn [& values]
(autology.core/evaluate
body
(reduce (fn [acc-env [s v]]
(assoc acc-env s v))
env
(zipmap params values)))))))
;; rebind `*i*` to be a new interpreter with the
;; `:atl/eval-list` section replaced with a version that
;; includes our lambda handling special form.
*i* (replace-marker *i* :atl/eval-list
(list :atl/eval-list
(concat (butlast original)
λ-form
(list (last original)))))
;; We can now immediately define functions since the
;; interpreter will have already been updated to evaluate the
;; remaining bindings like this one.
double (λ (n)
(+ n n)))
;; Finally we can invoke our new function!
(double (double (double (double (double 1.3125))))))
After that, pretty much anything you want!
- Why not switch form applicative order evaluation to normal order evaluation?
- Maybe modify the language so it uses a continuation passing style?
- Define a metacircular Lisp inside your program which has access to it's own interpreter as well at the Autology interpreter.
clojure -X:test