2014-10-27 72 views
1

根据规范,def应该在当前ns中实现var(即* ns *)。但是,下面的代码看起来不像这样:Clojure使用什么命名空间来定义

(ns namespace-b) 

(defn def_something [] 
    (ns namespace-a) 
    (println *ns*) ;prints namespace-a as it should 
    (def something 1) 
) 

(def_something) 

(println namespace-b/something) ; prints 1 
(println namespace-a/something) ; throws 

我错过了什么?

注:

  • defn是只用于清晰度。定义和运行匿名函数也同样适用。
  • 我知道使用def里面的函数可能不是很习惯。然而,这只是我遇到的一个更大问题的精髓。
+0

不仅是函数里面的'def'不是惯用的,它不会做你期望的(正如你正在学习的那样)。最好直接使用'intern',或者至少使用一个宏,以便def在适当的命名空间中完成它的工作。 – noisesmith 2014-10-27 16:43:41

回答

3

解析器已经实习生在编译时的VAR到当前的命名空间,虽然不会立即被束缚:

(defn dd [] (def x 0)) 
x ;; => #<Unbound Unbound: #'user/x> 

相关的代码可以发现here,与第二个参数为lookupVar,触发前述的不存在的变量here的实习。

解析then generates an expression that references the previously created var,所以表达式逻辑永远不会离开当前的命名空间。

TL; DR:def是编译器以特殊方式处理的东西。

+0

顺便说一句,这是多么混乱的编译器代码... – xsc 2014-10-27 14:09:05

+0

我明白,但这是否意味着应该纠正规范?我没有在任何地方看到'def'的正确描述。 – 2014-10-27 17:01:28

+1

另一个相关的问题:你从头开始Leinigen项目。定义函数'myfunc'并从'-main'中调用它。这样做虽然可行,但它不应该注意到,Leiningen在新创建的“用户”名称空间中运行了-main,它不包含“myfunc”既不作为别名也不作为引用。这只是一个奇怪的'def'行为的后果,这种代码正在工作。而且,如果你在-main中运行(eval'(myfunc)),它会崩溃(因为它应该是IMO)。现在如果你直接运行代码(没有Leiningen),一切正常。我明白这里发生了什么,但是男人,这很丑陋。 – 2014-10-27 17:13:34

1

了解def的关键是它是一个宏。这意味着它不会在运行时解析命名空间或创建绑定,但是在编译代码的时候事先要做。

如果调用一个调用def的函数,那么对def的调用已解析为使用定义函数的名称空间。同样,如果您在函数体内调用函数,则要在调用该函数的名称空间内的编译时解析要调用的函数。

如果您希望在运行时将值绑定到名称空间,则应该使用函数intern,该函数允许您明确地将名称空间设置为mutate。所有这一切都说,命名空间变异就是这样,它是过程式的,并且不是线程安全的,并且不像Clojure提供的其他选项那样具有很好的声明性语义。我强烈建议找到一种方法来表达不涉及不安全运行时突变的解决方案。