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.

Original source