Clojure:如果在源代码中定义之前调用函数,为什么函数应该是`declare`

nan*_*ix9 12 clojure

在Clojure中,如果在定义之前调用函数,例如

(foo (bar 'a))

(defn bar [] ...)
Run Code Online (Sandbox Code Playgroud)

它没有编译.应该补充一点

(declare bar)
Run Code Online (Sandbox Code Playgroud)

之前(foo (bar 'a)).为什么Clojure是这样设计的?我的意思是,在大多数语言中,除了C/C++,如Java,Python,PHP,Scala,Haskell甚至其他Lisps,特别是在动态类型语言中,不需要函数声明,也就是说,函数定义可以放在在通话之前或之后.我觉得使用起来很不舒服.

nha*_*nha 18

Clojure进行单遍编译(我简化,阅读以下链接):

因此,如果您只阅读一次源代码,从上到下,您就无法获得前向声明等内容并安全地执行,这似乎是合乎逻辑的.

引用Rich(第一个链接):

但是,这里应该发生什么,当编译器从未见过bar?

`(defn foo [] (bar))`
Run Code Online (Sandbox Code Playgroud)

或在CL中:

`(defun foo () (bar))`
Run Code Online (Sandbox Code Playgroud)

CL很乐意编译它,如果从未定义bar,则会发生运行时错误.好吧,但是,在编辑期间,什么具体化的东西(符号)用于吧?读取表单时它所占用的符号.因此,当您收到运行时错误并意识到该条在您忘记导入的另一个包中定义时会发生什么.你试图导入other-package和BAM !,另一个错误 - 冲突,其他包:bar与read-in-package冲突:bar.然后你去学习uninterning.

在Clojure中,表单不会编译,您会收到一条消息,并且没有为bar实现内容.您需要其他命名空间并继续.

我非常喜欢这种体验,因此做出了这些权衡.使用非实习阅读器和仅仅在定义/声明方面实现了许多其他好处.我不倾向于放弃它们,也不倾向于前面提到的好处,以支持循环引用.

  • 太棒了,你挖出了这些旧的 HN 帖子以及 Rich Hickey 的解释。很长一段时间以来我一直想再次找到他们。 (2认同)
  • 这也意味着您必须以一致的方式(即从底部到顶部)读取代码.如果你看一个函数,那么你知道它里面的所有声明都会在命名空间中位于它之上,这只是一件小事,但在阅读代码时它很有用. (2认同)