对于clojure defn参数,不允许使用^ char类型提示

noa*_*hlz 7 clojure

观察以下repl会话:

user=> (set! *warn-on-reflection* true)
true

user=> (defn blah [s] (for [c s] (if (Character/isDigit c) true false)))
Reflection warning, NO_SOURCE_PATH:1:31 - call to isDigit can't be resolved.
Reflection warning, NO_SOURCE_PATH:1:31 - call to isDigit can't be resolved.
#'user/blah

user=> (blah "abc123abc")
(false false false true true true false false false)

user=> (defn blah [s] (for [^char c s] (if (Character/isDigit c) true false)))
#'user/blah

user=> (blah "abc123abc")
(false false false true true true false false false)
Run Code Online (Sandbox Code Playgroud)

所以我们使用了一种类型的提示^char来摆脱反射 - 很棒.现在在函数参数中尝试相同的事情:

user=> (defn blah-c [c] (if (Character/isDigit c) true false))
Reflection warning, NO_SOURCE_PATH:1:22 - call to isDigit can't be resolved.
#'user/blah-c

user=> (defn blah-c [^char c] (if (Character/isDigit c) true false))
CompilerException java.lang.IllegalArgumentException: Only long and double primitives are supported, compiling:(NO_SOURCE_PATH:1:1) 

user=> (defn blah-c [^Character c] (if (Character/isDigit c) true false))
#'user/blah-c
user=> (blah-c \1)
true
user=> (blah-c \a)
false
Run Code Online (Sandbox Code Playgroud)

我知道Clojure 只支持数字基元的长或双类型提示,而Java char是一种数字数据类型 - 无需解释.但上面似乎不一致 - 类型提示^char允许在第一个函数内部for,但不在函数签名中blah-c,我必须指定Character.这是什么原因(即从编译器实现的角度来看)?

A. *_*ebb 3

在类型提示for表达式中,您将标记c为 achar作为对编译器的提示。当编译器为其发出(静态)方法时isDigit,就会知道您想要接受 a 的版本char(而不是可能的int版本)。字节代码被发送到实现接口O(单Object参数)版本的函数对象中IFn(默认情况下所有参数都被装箱)。

在另一种情况下blah-c,字节码需要发送到实现不存在的C(例如, for char)接口版本的函数对象IFn。每个原语都可以有接口吗?当然可以,但是没有。对于每种可能的组合?由于组合爆炸,不可行。

你可能会说,好吧,为什么不直接发送blah-cO接口呢?这将违背函数参数上类型提示的要点,即避免装箱/拆箱,因为随后必须对字符基元进行装箱才能进行调用。函数参数的类型提示的目的不仅仅是为了避免反射。如果你想避免在这里反射,那么你不会标记函数参数,而是在调用之前将其强制到块char中。letisDigit

请注意,在clojure.lang.IFn中,枚举接口(当前)仅限于任意数量的对象(装箱类型)以及最多四种doublelong类型的组合。提供doublelong版本作为优化,以避免在基元上编写性能关键代码时装箱/拆箱,并且应该足以满足大多数用途。