Syntax-quote in Clojure is an expressive reader macro for constructing syntax.
The library backtick
demonstrates how to write syntax-quote as a macro.
Both approaches are ripe for optimization—take
these programs that both evaluate to []
:
'`[]
;=> (clojure.core/apply
; clojure.core/vector
; (clojure.core/seq (clojure.core/concat)))
(macroexpand-1 '(backtick/syntax-quote []))
;=> (clojure.core/vec (clojure.core/concat))
The reason syntax-quote’s expansion is so elaborate comes down to unquote-splicing (~@
) support.
When the program passed to ~@
is completely dynamic, the extra computation is essential.
(macroexpand-1 '(backtick/syntax-quote [1 ~@a 4]))
;=> (clojure.core/vec (clojure.core/concat [(quote 1)] a [(quote 4)]))
Since a
cannot be evaluated at compile-time, we can’t do much better than this in terms of code size.
The problem is that we’re stuck with this extra scaffolding even when ~@
is never used.
We don’t even need it for unquote (~
):
(macroexpand-1 '(backtick/syntax-quote [1 ~two ~three 4]))
;=> (clojure.core/vec
; (clojure.core/concat
; [(quote 1)] [two] [three] [(quote 4)]))
A more direct expansion would be ['1 two three '4]
.
I have implemented a branch of backtick
that optimizes syntax-quote
to only pay for the penalty of ~@
if it is used.
You can see the progression of the generated program becoming more dynamic as less static information can be inferred.
(macroexpand-1 '(backtick/syntax-quote []))))
;=> []
(macroexpand-1 '(backtick/syntax-quote [~local-variable]))))
;=> [local-variable]
(macroexpand-1 '(backtick/syntax-quote [~@local-variable]))))
;=> (clojure.core/vec local-variable)
Future work includes flattening spliced collections, such as:
(macroexpand-1 '(backtick/syntax-quote [1 ~@[two three] 4]))
;=> (clojure.core/vec
; (clojure.core/concat [(quote 1)] [two three] [(quote 4)]))
This should be simply ['1 two three '4]
.
PR’s to my branch are welcome for further performance enhancements.
Also, if you are interested in implementing these changes in a real Clojure implementation, jank is accepting contributions. It will directly help with ongoing efforts towards AOT compilation: