When to use ~'some-symbol in Clojure Macro?
clojure, clojurescript, functional-programming, lisp, macros
Solution
You can see an example in the Tupelo library with the Literate Threading Macro. We want the user to type the symbol `it` and have it recognized by the macro. Here's the definition:
(defmacro it->
"A threading macro like as-> that always uses the symbol 'it'
as the placeholder for the next threaded value "
[expr & forms]
`(let [~'it ~expr
~@(interleave (repeat 'it) forms)
]
~'it))
This is also referred to as an "anaphoric" macro. The user then creates code like this:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )
The user includes the special symbol `it` in their code, which the macro is expecting (& is required in this case).
This is a bit of a special case. In most cases, you want the macro to work no matter what symbols the user picks. That is why most macros use `(gensym...)` or the reader version with "#" suffix like this example:
(defmacro with-exception-default
"Evaluates body & returns its result. In the event of an exception, default-val is returned
instead of the exception."
[default-val & body]
`(try
~@body
(catch Exception e# ~default-val)))
This is the "normal" case, where the macro creates a "local variable" `e#` that is guaranteed not to overlap with any user symbol. A similar example shows the `spyx` macro creating a "local variable" named `spy-val#` to temporarily hold the result of evaluating the expression `expr`:
(defmacro spyx
"An expression (println ...) for use in threading forms (& elsewhere). Evaluates the supplied
expression, printing both the expression and its value to stdout, then returns the value."
[expr]
`(let [spy-val# ~expr]
(println (str (spy-indent-spaces) '~expr " => " (pr-str spy-val#)))
spy-val#))
Note that for the `(println...)` statement, we see the opposite syntax of `'~expr` -- but that is a topic for another day.
Problem
When I was reading The Joy of Clojure I came across some code. ``` (fn [~'key ~'r old# new#] (println old# " -> " new#) ``` What is the exact behaviour of this declaration `~'some-symbol`. Differences between `some-symbol#` and `'~another-symbol` or gensym? The Joy Of Clojure: (Did not understand) You’ll see the pattern `~'symbol` at times in Clojure macros for selectively capturing a symbolic name in the body of a macro. The reason for this bit of awkwardness[11] is that Clojure’s syntax-quote attempts to resolve symbols in the current context, resulting in fully qualified symbols. Therefore, `~'` avoids that resolution by unquoting a quote.