2015-06-07 12 views
5

Tôi không hiểu tại sao đoạn mã sau tạo ra một cảnh báo phản ánh:cảnh báo Reflection trong mã được tạo ra bởi Clojure vĩ mô

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

Sử dụng macroexpand-1 cho thấy các mã được tạo không có typehint, và nếu tôi viết giống nhau mã theo cách thủ mà không sử dụng một macro, không có cảnh báo phản ánh:

(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 chức năng cho thấy cảnh báo không chỉ là một cá trích đỏ, sự phản ánh thực sự xảy ra trong thời gian chạy:

(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

Khi tôi chạy 'macroexpand-1' dòng, tôi không thấy các loại dấu. Tôi nghĩ rằng có cái gì đó sai với vĩ mô, tuy nhiên; nó sẽ không chấp nhận các chuỗi chữ: '(my-macro" foo "); => java.lang.ClassCastException: java.lang.String không thể được chuyển thành clojure.lang.IObj'. – Mars

+0

Bạn sẽ thấy gợi ý loại. Bạn có chắc là bạn đặt '* print-meta *' thành true? – noziar

Trả lời

3

Thẻ phải là Biểu tượng chứ không phải Lớp. Vì vậy, các mã sau đây hoạt động:

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

này được thực sự ghi trong documentation of special forms:

: thẻ

một biểu tượng đặt tên một lớp hoặc một đối tượng Class cho biết kiểu Java của đối tượng trong var, hoặc giá trị trả về của nó nếu đối tượng là fn.

Thực tế là macroexpand-1 cho thấy một gợi ý kiểu đó là không hợp lệ nhưng trông giống hệt như một đúng là khá sai lầm :)

+1

Lưu ý rằng việc sử dụng backquote trên 'String' đúng hơn một chút, không phải là một câu trích dẫn thông thường. Nó sẽ giống nhau đối với các lớp trong java.lang, vì chúng được nhập vào tất cả các không gian tên và do đó các tham chiếu không đủ điều kiện, nhưng nếu bạn cố gắng sử dụng một lớp như ArrayList nó sẽ chỉ làm việc trong các không gian tên đã nhập java.util.ArrayList . – amalloy

Các vấn đề liên quan