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

时间:2013-06-26 15:04:43

标签: 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)

所以我们使用^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

我理解Clojure only supports long or double type hints for numeric primitives以及Java char是数字数据类型 - 无需解释。但上面似乎不一致 - 在^char内的第一个函数中允许使用类型提示for,但在blah-c的函数签名中不允许,我必须指定Character 。这是什么原因(即从编译器实现的角度来看)?

2 个答案:

答案 0 :(得分:3)

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

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

你可以说,为什么不向blah-c接口发出O?这将破坏函数参数上的类型提示点,这是为了避免装箱/拆箱,因为必须将字符原语装箱以进行调用。函数参数的类型提示点不仅仅是为了避免反射。如果你想在这里避免反射,那么你不会标记函数参数,而是在进行char调用之前将其强制转换为let块中的isDigit

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

答案 1 :(得分:0)

这是基于@A的评论。 Webb和@kotarak,据我所知。

这有两个方面:第一,为什么^char在某些情境中可用(例如在for中)?这不仅对于优化是必要的,而且对于正确的Java互操作也是必要的,如您的示例所示。此外,它看起来(对我来说)实现相对便宜,因为每个变量都是独立的,所以它可以自己处理。

功能定义不是这种情况,对于每种支持类型的组合,您必须定义一个新接口:例如

static public interface L{long invokePrim();}
static public interface D{double invokePrim();}
static public interface OL{long invokePrim(Object arg0);}
// ...
static public interface OLD{double invokePrim(Object arg0, long arg1);}
// all the way to
static public interface DDDDD{double invokePrim(double arg0, double arg1, double arg2, double arg3);}

每个新支持的类型都会添加许多新接口(指数增长)。这就是为什么只支持包含最多的原始类型的原因:longdouble