没有命名空间的取消引用

Unquoting without namespace

我需要在没有命名空间的情况下引用并将其与取消引用结合起来。类似于:

'[a b ~c]

不幸的是,取消引用仅适用于句法引用:

`[a b ~c]

但随后扩展为

[user/a user/b 7]

我想在没有命名空间的情况下进行扩展。

clojurians slack channel 上的建议如下:

对符号使用 "quote unquote" 的组合以摆脱命名空间:

`[~'a ~'b ~c]

这很完美。

作为参考,我一直在研究一种类似的功能,它不需要像 ~'a 那样对每个您希望保持不变的符号进行防御性处理。它尚未发布,但这是技术:

 ;-----------------------------------------------------------------------------
 (defn unquote-form?
   [arg]
   (and (list? arg)
     (= (quote unquote) (first arg))))

 (defn unquote-splicing-form?
   [arg]
   (and (list? arg)
     (= (quote unquote-splicing) (first arg))))

 (defn quote-template-impl
   [form]
   (walk/prewalk
     (fn [item]
       (cond
         (unquote-form? item) (eval (xsecond item))
         (sequential? item) (let [unquoted-vec (apply glue
                                                 (forv [it item]
                                                   (if (unquote-splicing-form? it)
                                                     (eval (xsecond it))
                                                     [it])))
                                  final-result (if (list? item)
                                                 (t/->list unquoted-vec)
                                                 unquoted-vec)]
                              final-result)
         :else item))
     form))

 (defmacro quote-template
   [form]
   (quote-template-impl form))

和单元测试以展示它的实际效果:

 ;-----------------------------------------------------------------------------
 (def vec234 [2 3 4])

 (dotest
   (is (td/unquote-form? (quote (unquote (+ 2 3)))))
   (is (td/unquote-splicing-form? (quote (unquote-splicing (+ 2 3)))))

   (is= (td/quote-template {:a 1 :b (unquote (+ 2 3))})
     {:a 1, :b 5})
   (is= (td/quote-template {:a 1 :b (unquote (vec (range 3)))})
     {:a 1, :b [0 1 2]})
   (is= (td/quote-template {:a 1 :b (unquote vec234)})
     {:a 1, :b [2 3 4]})

   (let [result (td/quote-template (list 1 2 (unquote (inc 2)) 4 5))]
     (is (list? result))
     (is= result (quote (1 2 3 4 5))))


   (is= (td/quote-template [1 (unquote-splicing vec234) 5]) ; unqualified name OK here
     [1 2 3 4 5])
   (is= (td/quote-template [1 (unquote-splicing (t/thru 2 4)) 5])
     [1 2 3 4 5])
   (is= (td/quote-template [1 (unquote (t/thru 2 4)) 5])
     [1 [2 3 4] 5])
   )

因此,您可以使用 quote-template 而不是 Clojure 的语法引号(即反引号)。然后,使用 unquoteunquote-splicing 将值插入到引用的模板中,而无需将名称空间添加到其他符号之前。

我知道这可能不是您正在寻找的答案,为什么不使用代码来构建您想要的东西呢?为什么要强迫它在一个表达式中完成?例如,您可以使用

(conj '[a b] c)