【问题标题】:Why defn seems to evaluate (def symbol) inside function's body?为什么 defn 似乎在函数体内评估(def 符号)?
【发布时间】:2017-04-18 01:47:56
【问题描述】:

在玩 Clojure 时,我编写了一个函数,它依赖于 someSymbol 在第一次运行时未定义,因为 (resolve someSymbol) 将返回 nil。事实证明,defn-ing 函数与 def 在其中某处导致符号被定义:

 (resolve 'someSymbol)
 (defn resolvePokus []
   (prn "I was evaluated")
   (def someSymbol 1)
   )
 (resolve 'someSymbol)

在 REPL 产量中:

nil
#'user/resolvePokus
#'user/someSymbol

这是否意味着在运行defn 时会评估一些特殊表达式?哪个?

简要查看 defn 的源代码并没有向我透露任何信息,除了这个核心函数中有一条 TODO 注释:)

【问题讨论】:

  • 非常清楚,顺便说一下,从函数内部重新绑定 Var 的根值是一种非常严重的代码气味。 (也就是说——这不是你应该要做的事情。
  • 此外,您实际上可能会查看 someSymbol 解析为什么——您会注意到,当它解析时,它是未绑定的——未绑定到 1——直到函数真正.
  • @AlanThompson,明确记录了它们不同的并发语义。 Re-def-ing 一个 var 是一个潜在的阻塞操作。
  • @AlanThompson, ...另外,与alter-var-root 不同,def 不提供原子性保证。见stackoverflow.com/questions/16447621/…
  • 仅供参考 - 输入 REPL 时将函数编译为字节码。编译器interns vars it encounters in def expressions in during compile time.

标签: clojure


【解决方案1】:

我假设你在 repl 中运行。我没有看到您描述的行为:

clj.core=> (resolve 'someSymbol)
nil
clj.core=>  (defn resolvePokus []
      #_=>    (prn "I was evaluated")
      #_=>    (def someSymbol 1)
      #_=>    )
#'clj.core/resolvePokus

clj.core=> (resolve 'someSymbol)
#'clj.core/someSymbol

; try to use it -> error "Unbound..."
clj.core=> someSymbol
#object[clojure.lang.Var$Unbound 0x542f6481 "Unbound: #'clj.core/someSymbol"]

clj.core=> (resolvePokus)             ; run the function
"I was evaluated"
#'clj.core/someSymbol
clj.core=>  (resolve 'someSymbol)     ; still can resolve
#'clj.core/someSymbol
clj.core=> someSymbol                 ; now we can use it
1

clj.core=> (declare xyz)          ; creates a var, but unbound
#'clj.core/xyz
clj.core=> (resolve 'xyz)         ; we can see resolve it
#'clj.core/xyz

clj.core=> xyz    ; try to use it -> error "Unbound"

#object[clojure.lang.Var$Unbound 0x2d1d436f "Unbound: #'clj.core/xyz"]

clj.core=> (def xyz 5)     ; define it
#'clj.core/xyz
clj.core=> (resolve 'xyz)  ; still can resolve
#'clj.core/xyz
clj.core=> xyz             ; now we can use it
5

因此,当我定义函数时,在输入最后的括号后,repl 会打印出 #'clj.core/resolvePokus 已定义但未定义 someSymbol。最后的resolve 调用仍然返回nil

但是,如果您进一步阅读,当 Clojure 第一次看到(def someSymbol 1) 时,它的作用似乎相当于(declare someSymbol)。当我手动 (declare xyz) 并稍后通过 (def xyz 5) 给它一个值时,您可以看到相同的行为

您可能希望look at this answer. 详细介绍“隐藏”变量以及它是如何在符号xyz 和值5 之间充当匿名中介的。


附:上面的例子是在 Ubuntu 16.04、Clojure 1.8、Java 1.8 上运行的

【讨论】:

  • 哦,对不起。我错过了粘贴 - 最后一行有 someSymb 而不是 someSymbol。正在修正中...
  • 你确定 final resolve 仍然返回 nil 吗?我的 REPL 不这么认为: user=> (resolve 'someSymbol) nil user=> (defn resolvePokus [] #_=> (prn "I was evaluation") #_=> (def someSymbol 1) #_=> ) #'user/resolvePokus user=> (resolve 'someSymb) nil user=> (resolve 'someSymbol) #'user/someSymbol user=> (resolve 'someSymbol) #'user/someSymbol
  • 仔细观察(也许刷新页面)。 (resolve 'someSymbol) 除第一次外不会返回 nil。但是,当 (defn resolvePokus ...) 完成时,repl 不会打印任何关于 someSymbol 的信息。
  • 好吧,我这样做了,第一个解析确实返回 nil(如预期的那样)。然后我明白(现在),在 defn 中只发生了一些必要符号的声明(符号存在,但没有值存储在底层 var 中)并且最终解析确实确认了符号的存在(虽然它仍然未绑定)。正确的?感谢您和 Charles D. 的时间
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-15
  • 2011-09-18
  • 1970-01-01
  • 2019-05-27
相关资源
最近更新 更多