Clojure:解析声明的符号

7

当检查符号是否可以解析时,我遇到了一些奇怪的行为。

user=> ok
CompilerException java.lang.RuntimeException: Unable to resolve symbol: ok in this context, compiling:(NO_SOURCE_PATH:0)
user=> (resolve 'ok)
nil
user=> (if (resolve 'ok) "bla" (def ok 'ok))
"bla"
user=> ok
#<Unbound Unbound: #'user/ok>
user=> (def ok 'ok)
#'user/ok
user=> ok
ok

有人能告诉我这可能来自哪里吗?这种行为是否被预期?

你使用的Clojure版本是什么?我在1.2.1上没有看到这个。 - okonomichiyaki
1
@spacemanaki:有趣,我在1.2.1和1.3中都复现了这个问题。肯定发生了一些奇怪的事情。 - Goran Jovic
我的错误,第二次引用 ok 导致异常而不是返回一个 #<Unbound...> 对象,我没有注意到。对于我来说,在 1.2.1 版本中,if 内部的 (resolve 'ok) 确实导致了 ok 的声明。 - okonomichiyaki
你会发现这个答案对另一个问题也很有用。他的函数bounded?似乎可以解决你的问题。我在你的例子中用它替换了resolve,并且它起作用了。至于这种行为,我希望我们能得到一个答案。 - Goran Jovic
2个回答

4

(def ok "whatever") 在编译时创建了一个名为 ok 的变量。编译器在扫描整个表单以进行编译时,发现您将定义一个名为 ok 的变量,并在实际执行表单之前为您创建它(没有绑定)。当实际执行 def 表单时,表达式的运行时值将被分配给变量 user/ok。在您的示例中,这从未发生,因为该变量已经被创建,而且 if 分支走向另一个方向。

使用 bound? 作为替代方案是一个糟糕的想法,因为它测试的是完全不同的东西:命名变量(必须存在)是否具有绑定,无论是永久还是线程本地的。


听起来很合理。但是为什么(if (resolve 'x) x (def x 'x))会产生CompilerException java.lang.RuntimeException: Unable to resolve symbol: x in this context, compiling:(NO_SOURCE_PATH:1)的结果呢?或者他是在创建变量之前尝试评估x吗? - Dominik G

1

因为我只在宏内部使用它,所以现在我使用它如下

(defmacro bla [x]
    (if (resolve x) x `(def ~x '~x)))

现在它可以工作了,因为def在引号形式内部,并且在解析后进行评估。


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