^char type hint not permitted for clojure defn parameter

Observe the following repl session:

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)

So we used a type hint of ^char to get rid of reflection - great. Now try the same thing in a function parameter:

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

I understand that Clojure only supports long or double type hints for numeric primitives, and that a Java char is a numeric data type - no need to explain that. But the above seems inconsistent - type hinting ^char is allowed in the first function inside the for , but not in the function signature of blah-c , where I had to specify Character . What the reason for this (ie from the compiler implementation perspective)?


In the type-hinted for expression you are tagging c as a char as a hint to the compiler. When the compiler emits the (static) method for isDigit it then knows you want the version accepting a char (as opposed to possibly the int version). The byte code is emitted into a function object implementing the O (single Object argument) version of the IFn interface (all arguments are boxed by default).

In the other case, blah-c , the byte code would need to be emitted to a function object implementing a non-existent C (for example, for char ) version of the IFn interface. Could there be interfaces for each primitive? Sure, but there is not. For each possible combination? Not feasible, due to combinatorial explosion.

You could say, well, why not just emit blah-c to an O interface? This would defeat the point of the type hint on the function argument, which is to avoid boxing/unboxing, as the character primitive would then have to be boxed to make the call. The point of type hints on function arguments is not merely to avoid reflection. If you want to avoid reflection here, then you would not tag the function argument but instead coerce it into a char in a let block before making the isDigit call.

Note in clojure.lang.IFn, the enumerated interfaces are (currently) limited to any number of Objects (boxed type) and up to four combinations of double and long types. The double and long versions are provided as an optimization to avoid boxing/unboxing when writing performance critical code on primitives and should be enough for most purposes.


This is based on the comments from @A. Webb and @kotarak, as far as I can understand them.

There are two aspects to this: first, why the ^char is available in some contexts (eg in for )? This, is necessary not only for optimizations, but for correct Java interop as your example shows. Also, it looks (to me) relatively cheap to implement, as each variable is independent, so it can be processed by itself.

This is not the case with function definition, where for each combination of supported type you have to define a new interface: eg

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);}

Each new supported type would add a lot of new interfaces (the exponential growth). That's why only the most encompassing primitive types are supported: long and double .

链接地址: http://www.djcxy.com/p/14488.html

上一篇: 将数组转换为.ini文件

下一篇: ^ char类型提示不允许clojure defn参数