我不清楚为什么在下面的代码片段中,foo是在“user”命名空间中定义的,而不是在绑定闭包中绑定到* ns *的那个。有人可以解释我错过的东西吗?
$ clj Clojure 1.4.0 user=> (let [nspace (create-ns (gensym "sandbox"))] (binding [*ns* nspace] (print (ns-name *ns*)) (def foo 6))) sandbox3#'user/foo user=> foo 6 user=> (in-ns 'sandbox3) # sandbox3=> foo CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:0) sandbox3=> (def bar 7) #'sandbox3/bar sandbox3=> bar 7 sandbox3/user=> (in-ns 'user) # user=> foo 6 user=> bar CompilerException java.lang.RuntimeException: Unable to resolve symbol: bar in this context, compiling:(NO_SOURCE_PATH:0)
答案 0 :(得分:5)
def
将创建Var的命名空间在编译时确定。然后绑定无效(binding
引入运行时绑定)。
def
只应在顶级或顶级let
表单中使用。另一个偏离简单的顶级def
,一个人应该更加谨慎。例如,binding
在许多方面肯定类似于顶级let
,并且def
表达式的值部分将使用预期的线程局部绑定进行评估:
user=> (let [nspace (create-ns (gensym "foo"))]
(binding [*ns* nspace]
(def foo *ns*)))
#'user/foo
user=> foo
#<Namespace foo3>
但是,当执行到达def
表单时,首先创建Var的方式完全不受运行时状态的影响。
要动态创建Vars,请使用intern
- 这是一个具有完全可预测行为的常规函数。
答案 1 :(得分:3)
您需要使用eval:
(let [nspace (create-ns (gensym "sandbox"))]
(binding [*ns* nspace]
(print (ns-name *ns*))
(eval '(def foo 6))))
eval
关注*ns*
,但大部分Clojure都没有。
对于上下文,我一直与op相对应,他不想要一个真正的沙盒,因为他信任进入他的系统的代码,他只需要一种方法来评估不同命名空间内的输入代码!
所以,如果你是这个问题的新手并且你不是sethwm,请参考Marczyk的回答。这个解决方案非常特定于OP的用例,而且通常不是做任何事情的好方法。