【问题标题】:What namespaces Clojure uses for def-ingClojure 使用哪些命名空间进行定义
【发布时间】:2014-12-22 16:45:37
【问题描述】:

根据规范,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 可能不是很惯用。然而,这只是我遇到的一个更大问题的精华。

【问题讨论】:

  • 不仅函数内部的def 不是惯用的,而且它不会按照您的预期执行(正如您正在学习的那样)。最好直接使用intern,或者至少使用一个宏,以便 def 在正确的命名空间中执行其操作。

标签: clojure namespaces function defn


【解决方案1】:

解析器在编译时已将 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 是编译器以特殊方式处理的东西。

【讨论】:

  • 我明白,但这是否意味着应该更正规范?我在任何地方都没有看到对“def”的正确描述。
  • 另一个相关问题:您从头开始 Leinigen 项目。定义函数'myfunc'并从'-main'调用它。尽管 IMO 不应该这样做,但它可以工作 - 请注意,Leiningen 在新创建的“用户”命名空间中运行 -main,该命名空间不包含“myfunc”,既不作为别名,也不作为参考。这只是奇怪的“def”行为的结果,这样的代码正在工作。此外,如果您在 -main 中运行 (eval '(myfunc)),它会崩溃(正如 IMO 应该的那样)。现在,如果您直接运行代码(没有 Leiningen),一切正常。我明白这里发生了什么,但是伙计,这很丑。
  • myfunc 定义在什么命名空间中?为什么从-main 看不到它?我不明白。
  • 如果myfunc 定义在与-main 相同的命名空间中,这是正常的,也是预期的行为。名称是根据编译代码的命名空间解析的。
  • 如果您检查代码:gist.github.com/tomaskulich/57fba595484e5e8e1a82,您可能会明白我在说什么。这里有趣的是,如果您使用 clojure 解释器运行 -main 函数(即您更改代码,例如gist.github.com/tomaskulich/5df430449e9b027fe893),一切正常(因为解释器不会创建自己的命名空间来运行代码)。
【解决方案2】:

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

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

如果您想在运行时将值绑定到命名空间,您应该使用函数intern,它可以让您明确地将命名空间设置为变异。

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

【讨论】:

    猜你喜欢
    • 2011-08-31
    • 1970-01-01
    • 2017-01-27
    • 2012-06-16
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 2019-07-20
    相关资源
    最近更新 更多