何时在Clojure宏中使用`~'some-symbol`?

3
当我阅读《Clojure之乐》时,我遇到了一些代码。
(fn [~'key ~'r old# new#]
                  (println old# " -> " new#)

这个声明~'some-symbol的确切行为是什么? some-symbol#'~another-symbol或gensym之间的区别是什么?
《Clojure的乐趣》:(不理解)
引用如下:
您会在Clojure宏中时而看到模式~'symbol,用于选择性地捕获宏主体中的符号名称。这种笨拙的方式[11]的原因是Clojure的语法引用试图解析当前上下文中的符号,导致完全限定符号。因此,~'通过取消引号来避免解析。

2
虽然我喜欢《Clojure之乐》,但它是一本相对高级的书籍,你可能需要在阅读2-4本初级/中级书籍后重新阅读它。这是我读过的第一本Clojure书籍,许多主题在没有足够的Clojure背景知识的情况下很难理解。 - Alan Thompson
1个回答

6

您可以在Tupelo库中看到一个示例,其中包含文学线程宏。我们希望用户键入符号it并被宏识别。以下是定义:

(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))

这也被称为“指代宏”。用户随后创建以下代码:
(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." )

用户在代码中使用了特殊符号it,而宏期望的是(& 在这种情况下是必需的)。

这是一种特殊情况。在大多数情况下,您希望宏无论用户选择什么符号都能正常工作。这就是为什么大多数宏使用(gensym...)或带有"#"后缀的读取器版本的原因,就像这个例子:

(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)))

这是“正常”情况,宏将创建一个名为 e# 的“局部变量”,保证不会与任何用户符号重叠。类似的示例显示 spyx 宏创建一个名为 spy-val# 的“局部变量”,用于暂时保存评估表达式 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#))

请注意,在(println...)语句中,我们看到了与'~expr相反的语法--但这是另一个话题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接