Clojure 宏中符号分辨率的正确处理
假设我想制作一个Clojure宏,该宏执行以下操作:Clojure 宏中符号分辨率的正确处理,clojure,Clojure,假设我想制作一个Clojure宏,该宏执行以下操作: If x is a list calling the function "bar" return :foobar else return x as a string 但是,bar未定义;相反,它仅在宏中内部使用,如下所示: (foo (bar)) :foobar (foo 1) "1" 我们可以这样做: (defmacro foo [x] (if (and (coll? x) (= (first x) 'bar))
If x is a list calling the function "bar"
return :foobar
else
return x as a string
但是,bar
未定义;相反,它仅在宏中内部使用,如下所示:
(foo (bar))
:foobar
(foo 1)
"1"
我们可以这样做:
(defmacro foo [x]
(if (and (coll? x) (= (first x) 'bar))
:foobar
(str x)))
这对于(bar)
大小写以及文本都非常有效。但是,符号不能按预期工作,只能给出符号名称而不是其关联值:
user=> (def y 2)
#'user/y
user=> (foo y)
"y"
在将函数传递给str
之前,可以在x
上调用eval
函数,但在let
中使用函数时会出现问题:
user=> (let [a 3 b (foo a)] b)
java.lang.UnsupportedOperationException: Can't eval locals (NO_SOURCE_FILE:89)
这个问题可能与符号解析有关,因此我们可以尝试使用语法引号解决:
(defmacro foo [x]
`(if (and (coll? '~x) (= (first '~x) '~'bar))
:foobar
(str ~x)))
现在,问题在于(foo(bar))
,因为它将else子句扩展为(clojure.core/str(bar))
,这会引发异常,因为未定义bar
。然后我试着用eval
做一些恶作剧:
(defmacro foo [x]
`(if (and (coll? '~x) (= (first '~x) '~'bar))
:foobar
(eval '(str ~x))))
但这不适用于let
绑定:
user=> (let [a 1 b (foo a)] b)
java.lang.Exception: Unable to resolve symbol: a in this context (NO_SOURCE_FILE:153)
所以我在这里真的不知所措。似乎解决一个问题会破坏另一个问题。是否有更好、更简单的方法制作此宏,使其在以下情况下工作:
- 在
绑定中让
- 使用
(条形)
- 用符号
另外,如果有人想知道我为什么要这么做,我正在为雅虎的YQL服务开发一个DSL,我希望能够做类似于
(选择(表:t)…
)的事情,但我需要能够传递符号以及文字。我相信这应该可以
(defmacro foo [x]
(if (and (coll? x) (= (first x) 'bar))
:foobar
`(str ~x)))
我想我并不认为语法只引用一部分表达式,但似乎是这样做的。谢谢