Backquote/tilde in and outside of macro definitions

Can someone help me understand how backquote works? I shake with fear every time I use it. Part of my confusion is that it behaves subtly different from the backquote in Common Lisp which obviously inspired it.

For example, what is the different between the following two expressions.

(bdd-canonicalize
 `(~'and :sigma (~'not (~'or ~@existing-labels))))

vs

(bdd-canonicalize
 `(and :sigma (not (or ~@existing-labels))))

In Common Lisp there would be no difference between the two expressions, of course in CL we use a , rather than ~ in all cases.

1 Like

The official reference section is pretty short but hits all the highlights: https://clojure.org/reference/reader#syntax-quote

One of the main differences between quote and syntax-quote is that “syntax-quote resolves the symbol in the current context, yielding a fully-qualified symbol”. So in the second example, you’d get clojure.core/and, clojure.core/not, etc. In the first example the ~ indicates to evaluate (so not following symbol resolution of syntax-quote) the expression 'and, which since quoted yields the unqualified symbol and. That’s it.

I find that the mandatory use of ~' makes the use of backquote dubious. I.e., my purpose of using the backquote is to build an s-expression by providing a template and interpolating values into it. Thus the second form is more readable, and is the idiom I’d use in Common Lisp.
The first form, which is necessary so that and, not, and or get the same semantics as if they appeared just in a quoted list, is (to me) more difficult to read and understand. Although it is still more readable than constructing the form avoiding backquote.

(bdd-canonicalize
  (list 'and :sigma (list 'not (cons 'or exiting-labels))))

If I made the underlying DLS interpreter treat clojure.core/and the same as unqualified and, then it would no longer matter which syntax be used in the backquoted form. Perhaps that would make the code more readable???

Sometimes you want to quote the symbol itself, like 'and as a symbol, unqualified. And sometimes you want to quote the symbol that is the fully qualified reference to the Var it defines like clojure.core/and which you can do with `and

The two seem necessary to me. The latter is for avoiding unhygienic macros.

And sometimes, you want a hybrid of both, at a granular level, that’s when you’d start doing `~'and

If you want common lisp behavior, you can use quote by itself. But it won’t be hygenic. And all symbols will resolve at the site of where the macro is used, not defined.

But I’m not writing a macro. I’m must building a hierarchical list DSL by interpolation.

You might want to check out this library:

https://github.com/brandonbloom/backtick

which includes a template macro which works like syntax quote without the symbol resolution.

2 Likes

I think syntax quote and quote are the two most common ways of doing this and have the tradeoffs that have been well explored here. Another option that is rarely used but in core is the clojure.template namespace https://clojure.github.io/clojure/clojure.template-api.html. Here it would be something like (clojure.template/apply-template '[labels] '(and :sigma (not (or labels))) [lbs]), however you won’t get ~@ splicing at the end, so maybe not an option here.

Another path to go is to remove the references to clojure.core/and, or, etc (using :refer-clojure :exclude [and or not] in your ns decl, then supply your own and, or, not as macros that expand to the form you want. Basically, let syntax quote resolve to your symbols, which are macros, which expand themselves. This is really hooking much more directly into the resolution, expansion, and evaluation available in Clojure. spec is implemented in this way (spec 1 and spec 2 vary quite a bit in how they work at this top level, but they are both leveraging the “trick” of symbolic names that are also macros that expand to something).

2 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.