1

根据规范,def应该在当前ns中实习var(即*ns*)。但是,以下代码看起来并不像它:

(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内部函数可能不是很惯用。然而,这只是我遇到的一个更大问题的精华。
4

2 回答 2

3

解析器已在编译时将 var 实习到当前命名空间,尽管它不会立即绑定:

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

相关的代码可以在这里找到,第二个参数用于lookupVar触发上述不存在的变量的实习

然后解析生成一个引用先前创建的 var的表达式,因此表达式逻辑永远不会离开当前命名空间。

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

于 2014-10-27T14:05:35.323 回答
1

要理解的关键def是它是一个宏。这意味着它不会在运行时解析名称空间或创建绑定,而是在编译代码时提前。

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

如果你想在运行时将值绑定到命名空间,你应该使用 function intern,它可以让你显式地将命名空间设置为 mutate。

综上所述,命名空间突变就是这样,它是程序性的,不是线程安全的,并且不像 Clojure 提供的其他选项那样具有良好的声明性语义。我强烈建议找到一种不涉及不安全运行时突变的方法来表达您的解决方案。

于 2014-10-27T20:11:29.573 回答