2013-06-26 13 views
7

osservare quanto segue sessione di repl:^char suggerimento non è consentito per il parametro clojure defn

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) 

così abbiamo usato un tipo di suggerimento ^char per sbarazzarsi di riflessione - grandi. Ora provate la stessa cosa in un parametro di funzione:

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 

Capisco che Clojure only supports long or double type hints for numeric primitives, e che un Java char è un tipo di dati numerici - non c'è bisogno di spiegare che. Ma quanto sopra sembra incoerente - il tipo di suggerimento ^char è consentito nella prima funzione all'interno dello for, ma non nella firma della funzione di blah-c, dove ho dovuto specificare Character. Qual è la ragione di questo (vale a dire dal punto di vista dell'implementazione del compilatore)?

+3

Esplosione combinatoria: per la compilazione dinamica, deve esistere un'interfaccia per ogni combinazione consentita di oggetti primitivi e oggetti. –

+0

Sembra una risposta fantastica se si desidera ampliarla, fornire riferimenti/esempi. – noahlz

+3

@noahlz cf. [clojure.lang.IFn] (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java) – kotarak

risposta

3

Nell'espressione tipo for si sta taggando c come char come suggerimento per il compilatore. Quando il compilatore emette il metodo (statico) per isDigit, allora sa che si desidera che la versione accetti una char (a differenza della versione int). Il codice byte viene emesso in un oggetto funzione che implementa la versione O (argomento singolo Object) dell'interfaccia IFn (tutti gli argomenti sono inseriti di default).

Nell'altro caso, blah-c, il codice byte dovrebbe essere emessa a un oggetto funzione che implementa una versione inesistente C (ad esempio, per char) dell'interfaccia IFn. Potrebbero esserci interfacce per ogni primitivo? Certo, ma non c'è. Per ogni combinazione possibile? Non fattibile, a causa dell'esplosione combinatoria.

Si potrebbe dire, beh, perché non emettere solo blah-c nell'interfaccia O? Questo annullerebbe il punto del suggerimento sul tipo di argomento della funzione, che è quello di evitare il pugilato/unboxing, in quanto la primitiva del personaggio dovrebbe quindi essere incassata per effettuare la chiamata. Il punto di suggerimento dei tipi sugli argomenti della funzione non è semplicemente quello di evitare la riflessione. Se si desidera evitare la riflessione qui, non si codificherà l'argomento della funzione, ma invece lo si costringerà in un char in un blocco let prima di effettuare la chiamata isDigit.

Nota in clojure.lang.IFn, le interfacce enumerate sono (attualmente) limitate a qualsiasi numero di oggetti (tipo in scatola) e fino a quattro combinazioni di tipi double e long. Le versioni double e long vengono fornite come ottimizzazione per evitare il boxing/unboxing durante la scrittura di codice critico delle prestazioni su primitive e dovrebbero essere sufficienti per la maggior parte degli scopi.

0

Questo si basa sui commenti di @A. Webb e @kotarak, per quanto posso capirli.

Esistono due aspetti: primo, perché lo ^char è disponibile in alcuni contesti (ad esempio in for)? Questo, è necessario non solo per le ottimizzazioni, ma per correggere l'interoperabilità Java come mostra il tuo esempio. Inoltre, sembra (per me) relativamente poco costoso da implementare, poiché ogni variabile è indipendente, quindi può essere elaborata da sola.

Questo non è il caso della definizione di funzione, dove per ciascuna combinazione di tipo supportato è necessario definire una nuova interfaccia: ad es.

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

Ogni nuovo tipo supportato aggiungerebbe molte nuove interfacce (la crescita esponenziale). Ecco perché sono supportati solo i tipi primitivi più comprensivi: long e double.

+0

Che dire di 'for'? – noahlz

+0

Bene, io dico qualcosa sul contesto 'for'. Immagino che la differenza cruciale sia che non si definisce un nuovo 'fn' lì, quindi non è necessario implementare' IFn'. Di nuovo, la risposta di A. Webb è più piena e sicuramente più informata. – ivant

Problemi correlati