将* ns *绑定到其他内容后修改变量

时间:2014-02-14 19:11:16

标签: clojure

我不清楚为什么在下面的代码片段中,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) 

2 个答案:

答案 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都没有。

编辑:是的,我只是建议一个人使用eval:p

对于上下文,我一直与op相对应,他不想要一个真正的沙盒,因为他信任进入他的系统的代码,他只需要一种方法来评估不同命名空间内的输入代码!

所以,如果你是这个问题的新手并且你不是sethwm,请参考Marczyk的回答。这个解决方案非常特定于OP的用例,而且通常不是做任何事情的好方法。