2015-06-07 15 views
5

Nie rozumiem dlaczego poniższy kod generuje ostrzeżenie refleksji:ostrzeżenie Odbicie w kodzie generowane przez Clojure makro

(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. 

Korzystanie macroexpand-1 pokazuje, że wygenerowany kod nie mają typehint, a jeśli piszę to samo Kod ręcznie bez użycia makra, nie ma ostrzeżenia odbicie:

(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 

benchmarking funkcji pokazuje, że ostrzeżenie to nie tylko czerwony śledź, odbicie w rzeczywistości występuje w czasie wykonywania:

(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

Kiedy uruchomić linię 'macroexpand-1', nie widzę typ Wskazówka. Myślę jednak, że coś jest nie tak z makrem; nie przyjmuje łańcuchów literowych: '(my-macro" foo "); => java.lang.ClassCastException: java.lang.String nie może być rzutowany na clojure.lang.IObj'. – Mars

+0

Powinieneś zobaczyć wskazówkę dotyczącą typu. Czy na pewno ustawisz "* print-meta *" na wartość true? – noziar

Odpowiedz

3

Znacznik powinien być symbolem, a nie klasą. Więc następujący kod działa:

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

To jest rzeczywiście stwierdzono w documentation of special forms:

: tag

symbol nazewnictwa klasy lub obiektu klasy wskazującą typ Java obiektu w var lub jego wartość zwracaną, jeśli obiekt jest obiektem o numerze fn.

Fakt macroexpand-1 przedstawia wskazówkę typu, który jest nieważny, ale wygląda dokładnie tak, jak poprawny jest dość mylący :)

+1

Należy zauważyć, że nieco bardziej poprawne jest użycie wstecznego napisu na "String", a nie zwykłego cytatu. Będzie to takie samo dla klas w java.lang, ponieważ są one importowane we wszystkich przestrzeniach nazw, a zatem niekwalifikowany odnośnik działa, ale jeśli spróbujesz użyć klasy takiej jak ArrayList, będzie działać tylko w przestrzeniach nazw, które zaimportowały java.util.ArrayList . – amalloy

Powiązane problemy