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