^ char hint type not allowed for clojure defn parameter

Pay attention to the following replication session:

user=> (set! *warn-on-reflection* true) true user=> (defn blah [s] (for [cs] (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 cs] (if (Character/isDigit c) true false))) #'user/blah user=> (blah "abc123abc") (false false false true true true false false false) 

So, we used the hint type ^char to get rid of the reflection - great. Now try the same thing in the 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 hints for numeric primitives , and Java char - numeric data type - no need to explain it. But the above seems inconsistent. In the first function inside the for type htting ^char allowed, but not in the signature of the blah-c function, where I needed to specify Character . What is the reason for this (i.e., in terms of compiler implementation)?

+7
source share
2 answers

In the type expression for you put c as a char as a hint to the compiler. When the compiler emits a (static) method for isDigit , it knows that you want the version to accept char (as opposed to possibly the int version). The bytecode is emitted into a function object that implements the O (single Object ) IFn interface (all arguments are marked by default).

In another case, blah-c the byte code must be sent to a function object that implements a non-existent version c (for example, for char ) of the IFn interface. Can there be interfaces for each primitive? Of course, but no. For every possible combination? Impossible due to combinatorial explosion.

You could say, well, why not just fix blah-c in the O interface? This will hit the hint point like the argument of the function, which is to avoid boxing / unboxing, since in order to reconcile the character, the primitive must be placed in the box. The point of allusion to the arguments of a function is not only to avoid reflection. If you want to avoid reflection here, then you do not mark the function argument, but instead take it to the char block in the let block before calling isDigit .

Note in clojure.lang.IFn the listed interfaces (currently) are limited to any number of objects (boxed) and to four combinations of double and long types. The double and long options are provided as optimizations to avoid boxing / unpacking when writing critical performance code on primitives and should be sufficient for most purposes.

+3
source

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

There are two aspects: firstly, why is ^char available in some contexts (e.g. in for )? This is necessary not only for optimization, but also for proper Java interaction, as your example shows. In addition, it looks (relatively to me) relatively cheap to implement, since each variable is independent, so it can be processed on its own.

This does not apply to the definition of a function, where for each combination of a supported type you must define a new interface: for example,

 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 will add many new interfaces (exponential growth). That's why only the most primitive primitive types are supported: long and double .

0
source

All Articles