【问题标题】:How does clojure's syntax-quote work?clojure 的语法引用是如何工作的?
【发布时间】:2010-09-13 20:58:56
【问题描述】:

clojure 中的各种特殊字符都是事物的缩写

(quote (a b))'(a b) 相同

你可以通过评估看到:

user> ''(a b)
(quote (a b))

这似乎是缩写形式的语法,我觉得这是个好主意。

但是语法引用 ` 似乎很特别。我想不出什么相当于

`(a b)

我会猜到像 (syntax-quote (a b)) 这样的东西,但它不起作用,如果我猜错了,我无法找出它的真正名称。

user> '`(a b)
(clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/a)) (clojure.core/list (quote user/b))))

有点神秘。

大概是读者在做一些特别的事情,也许是因为它需要知道命名空间?

有趣的是,语法引用中使用的特殊语法确实按我的预期工作:

user> '~a
(clojure.core/unquote a)
user> '~@a
(clojure.core/unquote-splicing a)
user> '~'a
(clojure.core/unquote (quote a))

除了这个:

user> 'a#
a#

我原以为会产生类似(unquote (gensym "a"))的东西

我确实意识到我在这里有点虚弱,应该去阅读代码。如果没有人愿意解释发生了什么或提供参考,谁能给我一个关于如何找到相关代码和寻找什么的提示?

【问题讨论】:

标签: clojure


【解决方案1】:

我认为没有与 quote 函数等效的语法引用。

Clojure 阅读器(目前)是用 Java 编写的。 Clojure 源代码中src/jvm/clojure/lang/LispReader.java 中的SyntaxQuoteReader 类可能是您想要阅读的内容。看起来比较复杂。您可以在那里看到它正在构建类似(seq (concat ...)) 的列表。

                ret = RT.list(SEQ, RT.cons(CONCAT, sqExpandList(seq)));

读者通常不会返回直接的 Clojure 代码,而是立即在 Java 领域做正确的事情。例如 '[1 2 3] 不会产生 Clojure 代码 (vector 1 2 3)。也许它可以以某种方式工作,但事实并非如此。阅读器只是创建并返回向量对象本身。

同样,SyntaxQuoteReader 在 Java 中立即执行了一些魔法来解析符号名称空间并自行创建 gensyms,它返回一些错乱且看起来复杂的 Clojure 代码,这些代码做正确的事情,但对人类来说并不容易读。是因为它必须是这样,还是因为在 Java 中这样做更容易,还是出于性能或其他原因,我不知道。同样,我不知道 quasiquote 是否可以在 Clojure 中以普通宏/特殊形式存在并且不存在,或者它是否根本不存在。我不明白为什么它不能。

同一文件中的WrappingReader 是处理' 的类(普通的旧quote)。您可以看到它只是将您传递的任何内容包装在一个包含符号quote 加上您的参数的列表中。这要简单得多。请注意,该类还处理@,因此'@foo 确实返回(deref foo)

This thread 可能会带来更多启示。

编辑

这是一个概念验证quasiquote 宏。请注意,此代码以一种可怕的方式依赖和滥用 Clojure 内部。请不要将其用于任何用途。

user> (defmacro quasiquote [x]
        (let [m (.getDeclaredMethod clojure.lang.LispReader$SyntaxQuoteReader 
                                    "syntaxQuote" 
                                    (into-array [Object]))]
          (.setAccessible m true)
          (.invoke m nil (into-array [x]))))
#'user/quasiquote
user> (let [x 123] `(x 'x ~x))
(user/x (quote user/x) 123)
user> (let [x 123] (quasiquote (x 'x ~x)))
(user/x (quote user/x) 123)

【讨论】:

  • 那是深奥的黑魔法!什么黑客!我没有更多的赞成票给你,但这很酷。
  • 关于 quasiquote 宏需要注意的一点是,如果您需要对隐式生成符号的支持,请在调用调用之前添加类似 (push-thread-bindings {(get-static-field clojure.lang.LispReader 'GENSYM_ENV) {}}) 的内容。
  • 我忘记了你必须将表达式包装到 try...finally 中,并在 finally 子句中调用 pop-thread-bindings 以干净利落地执行此操作。
【解决方案2】:

hiredman 已经实现了一个完全基于 clojure 的 syntax-quote 版本。不是为了胆小的人,而是一个很好的概念证明。

【讨论】:

    【解决方案3】:

    您似乎对宏语法有很好的掌握,所以我不能补充太多。

    programming clojure forums 有一些报道。 您可以随意阅读code here 查看第 352 行

    【讨论】:

    • 谢谢亚瑟,这些真的很方便。
    猜你喜欢
    • 2012-02-28
    • 2021-06-15
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    相关资源
    最近更新 更多