Clojure的编译器不是一个整程序编译器。编译的单位是顶级形式。所以当你说“编译这个文件”时,实际上要编译该文件中的每个顶级形式,编译器一次只能“看到”一个顶级形式。
函数是闭包,因此它们不仅仅是代码(像静态方法一样),它们是代码+封闭的值(像实例方法,其中的封闭值是实例的字段)。
例如
(fn [x] (fn [y] [x y]))
此代码包含一个内部函数和一个外部函数,编译后生成两个类。每次调用外部函数时,都会创建一个新的内部函数类的实例,该实例在构造时传递x的值,并将x存储在某些实例字段中。
使用静态方法实现函数还有另一个问题,即函数是一级值,而静态方法则不是。你无法将静态方法作为参数传递,等等。可用反射的Method和indy的MethodHandles提供一些静态方法的一级表示,但反射被认为是较慢的,并且MethodHandles在clojure最初创建时是不可用的。在任何情况下,这两种表示都无法实现clojure的函数接口IFn,因此需要大量工作才能使其工作(可能需要重新设计clojure调用函数的方式以使用invoke动态)。
在一般情况下,函数需要能够封装值(实例字段),并且需要有一种一级表示(实例),因此静态方法不可取。
然而,clojure编译器确实在可能的情况下会进行一些优化,它可以生成某个函数的静态方法,并在某些情况下将函数调用转换为静态方法调用。但它仍然生成了实例方法版本(这仅仅调用静态方法),因为编译器不能总是确定正在调用哪个静态函数,因此所有函数都必须支持相同的通用调用约定(成为一个实现IFn的实例)。
我一直致力于研究针对https://clojure.atlassian.net/browse/CLJ-701的方法(我尚未发布任何补丁,它仍然不起作用),这会导致编译器更积极地用静态方法替换某些函数定义和调用,并且完全消除了为这些函数生成单独类的需求,但它范围极广,并且仅限于在其中立即调用的函数,它们的定义没有逃离。这不会涵盖顶级定义,因为def使函数的值成为全局的,这有点像逃离的定义。