2015-06-07 16 views
5

Non capisco il motivo per cui il codice seguente produce un avvertimento riflessione:Riflessione di avvertimento in codice generato da Clojure macro

(set! *warn-on-reflection* true) 

(defmacro my-macro [k] `(.length ~(with-meta k {:tag String}))) 

(defn my-fun1 [k] (my-macro k)) 
;; Reflection warning, /tmp/form-init2370243866132870536.clj:1:18 - reference to field length can't be resolved. 

Utilizzando macroexpand-1 mostra che il codice generato ha il typehint, e se scrivo lo stesso codice manualmente senza utilizzare una macro, non c'è alcun avviso riflessione:

(set! *print-meta* true) 

(macroexpand-1 '(my-macro k)) 
;; (.length ^java.lang.String k) 

(defn my-fun2 [k] (.length ^String k)) 
;; All good, no reflection warning 

Analisi comparativa della funzione mostra che l'avviso non è solo una falsa pista, il riflesso si verifica effettivamente in fase di esecuzione:

(time (reduce + (map my-fun1 (repeat 1000000 "test")))) 
;; "Elapsed time: 3080.252792 msecs" 

(time (reduce + (map my-fun2 (repeat 1000000 "test")))) 
;; "Elapsed time: 275.204877 msecs" 
+0

Quando eseguo la linea 'macroexpand-1', non vedo il tipo suggerimento. Penso che ci sia qualcosa di sbagliato nella macro, comunque; non accetta stringhe letterali: '(my-macro" pippo "); => java.lang.ClassCastException: java.lang.String non può essere lanciato su clojure.lang.IObj'. – Mars

+0

Dovresti vedere il suggerimento sul tipo. Sei sicuro di aver impostato '* print-meta *' su vero? – noziar

risposta

3

Il tag deve essere un simbolo, non una classe. Così il seguente codice funziona:

(defmacro my-macro [k] `(.length ~(with-meta k {:tag `String}))) 

questo è in realtà affermato nella documentation of special forms:

: tag

un simbolo di denominazione di una classe o di un oggetto di classe che indica il tipo Java dell'oggetto nella var, o il suo valore di ritorno se l'oggetto è un fn .

Il fatto che macroexpand-1 mostra un tipo di suggerimento che non è valido, ma appare esattamente come quello corretto è del tutto fuorviante :)

+1

Si noti che è leggermente più corretto utilizzare un backquote su 'String' lì, non una citazione ordinaria. Sarà lo stesso per le classi in java.lang, poiché quelle sono importate in tutti gli spazi dei nomi e quindi il riferimento non qualificato funziona, ma se provate a usare una classe come ArrayList, funzionerà solo negli spazi dei nomi che hanno importato java.util.ArrayList . – amalloy

Problemi correlati