当使用 loop
,看起来 Clojure 编译器试图使用原生变量(原生类型)以获得更好的性能。从 "对 Java 原生类型支持" 部分。
let/loop 绑定的本地变量可以是原生类型,其可能是其初始化表达式的原生类型。
重新绑定原生本地变量的 recur 表达式不会进行装箱处理,并执行相同原生类型的类型检查。
下面的循环抛出语法错误。
(loop [x (pos? 1)]
(when-not x
(recur (first [false]))))
Syntax error (IllegalArgumentException) compiling fn* at (src/foo.clj:4:1).
recur arg for primitive local: x is not matching primitive, had: Object, needed: boolean
编译器似乎知道 pos?
返回一个原生类型的 boolean
,而 (first [false])
并不是(返回装箱的布尔类型 Boolean
?)。将 recur 参数包裹在 boolean
调用中可以成功编译。
(loop [x (pos? 1)]
(when-not x
(recur (boolean (first [false])))))
=> nil
然而,将 recur
参数更改为不同的函数确实很引人注目。
(loop [x (pos? 1)]
(when-not x
(recur (pos? x))))
Syntax error (IllegalArgumentException) compiling fn* at (src/foo.clj:4:1).
recur arg for primitive local: x is not matching primitive, had: Object, needed: boolean
由于编译器知道 pos?
在初始化表达式中返回原生类型,所以似乎它也应该知道在 recur 参数中也会返回原生类型。
与之前一样,将 pos?
的 recur 调用包裹在 boolean
中允许该表单编译。
(loop [x (pos? 1)]
(when-not x
(recur (boolean (pos? x)))))
=> nil
如果我将参数更改为一个字面量,我会得到语法错误。
(loop [x (pos? 1)]
(when-not x
(recur (boolean 1))))
Syntax error (IllegalArgumentException) compiling fn* at (src/foo.clj:4:1).
recur arg for primitive local: x is not matching primitive, had: Object, needed: boolean
但这只适用于数字。传递一个字符串、字符、向量,甚至是布尔值都可以编译。例如:
(loop [x (pos? 1)]
(when-not x
(recur (boolean true))))
=> nil
传递字面量布尔值不起作用。
(loop [x (pos? 1)]
(when-not x
(recur false)))
Syntax error (IllegalArgumentException) compiling fn* at (src/foo.clj:4:1).
recur arg for primitive local: x is not matching primitive, had: java.lang.Boolean, needed: boolean
pos?
是一个内联函数。在上面的情况下它扩展到 (. clojure.lang.Numbers (isPos 1))
。从 isPos
的源代码中,我们发现一个具有返回类型 boolean
的静态方法。
static public boolean isPos(Object x){
return ops(x).isPos((Number)x);
}
由于 isPos
是一个静态方法,编译器应该始终知道它的类型。从 类型提示
请注意,类型提示对于静态成员(或它们的返回值!)不是必需的,因为编译器始终有静态的类型。
但是,除了第一个示例和带有字面量示例之外,上述所有函数都调用返回原生布尔类型的静态 Java 方法。为什么编译器在上面的示例中没有看到原生类型返回值呢?