;;;; Macros: a defmacro in the file, called from the file. ;;;; ;;;; There is no interpreter, so every macro below was compiled into a shared ;;;; object and dlopened into the compiler before this file's first line was ;;;; parsed. What arrives here is the expansion; nothing at run time knows a ;;;; macro was involved. ;;;; ;;;; A macro takes one parameter, the slice of forms written at its call site, ;;;; and answers one form. That is where variadics come from in a language with ;;;; no &rest: (len args) is how many were written. ;; The simplest one there is: two forms, in order. It proves the call site's ;; arguments arrive as forms and come back as code. (defmacro both [args] `(do ~(at args 0) ~(at args 1))) ;; Splicing, which is the only reason ~@ exists: the body is however many forms ;; were written, and they go where a list is expected. (defmacro when2 [args] `(if ~(at args 0) (do ~@(form-rest args 1)))) ;; Expansion is not hygienic -- Common Lisp's rule and Clojure's, settled in ;; plan.org's open decision 2 -- so a macro that needs a name of its own asks ;; for one. gensym is a prelude function the loaded module runs while it runs, ;; and the name it answers starts with ~, which is a delimiter, so no symbol ;; the reader can produce is able to collide with it. ;; ;; Without this, `twice` would bind `tmp` and the caller's own `tmp` would be ;; shadowed inside it. The two calls below are the difference. (defmacro twice [args] (let [v (gensym)] `(let [~v ~(at args 0)] (+ ~v ~v)))) ;; A macro that answers a call to another macro. This costs the pre-pass ;; nothing: `both` is inside the quasiquote, so it is part of what this macro ;; *returns* and is expanded again after it returns, and `announce` can be ;; compiled without `both` existing. (defmacro announce [args] `(both (print "-> ") ~(at args 0))) ;; This is the one that makes the pre-pass a fixpoint rather than a sweep. The ;; call to `id` is not inside a quasiquote, so it runs while *this macro is ;; being compiled* -- which means `id` has to be compiled and dlopened first, ;; and until it is, `id` is a name nothing defines and this body will not ;; compile at all. So round 0 takes `id`, round 1 expands this against it, and ;; the module that finally answers a call holds both. (defmacro id [args] (at args 0)) (defmacro quiet [args] (id `(println "a macro that called a macro"))) ;; And a macro that expands into a call to itself, which is what every ;; conditional macro in every Lisp is. It gets smaller each time and stops at ;; the empty case, so the expander's fuel never comes into it. (defmacro all-of [args] (if (= (len args) 0) `true `(if ~(at args 0) (all-of ~@(form-rest args 1)) false))) (defn main [] i32 (both (print "a") (println "b")) (when2 true (print "c") (println "d")) (when2 false (println "not printed")) ;; 21 + 21. The argument is evaluated once, into the gensym'd binding. (print (twice 21)) (println "") ;; The caller's own `tmp` is untouched by the one the macro bound, because ;; the macro did not bind `tmp`. (let [tmp 5] (print (twice tmp)) (print " ") (print tmp) (println "")) (announce (println "announced")) (quiet) (print (all-of)) (println "") (print (all-of true true true)) (println "") (print (all-of true false true)) (println "") 0)