2011-10-13 21 views
21

tôi đang cố gắng để tạo ra một chút Clojure macro def sa String với một loại gợi ý:Clojure defmacro mất metadata

(defmacro def-string [name value] 
    `(def ^String ~name ~value)) 

(def-string db-host-option "db-host") 

Khi tôi macroexpand nó, kiểu gợi ý bị mất:

(macroexpand '(def-string db-host-option "db-host")) 
;=> (def db-host-option "db-host") 

Đừng bận tâm về sự khôn ngoan của loại gợi ý điều này.

Tại sao macro mất siêu dữ liệu? Làm cách nào để viết macro này hoặc bất kỳ macro nào bao gồm siêu dữ liệu?

Trả lời

32

^ là macro trình đọc. defmacro không bao giờ được nhìn thấy nó. Gợi ý được đưa vào danh sách (unquote name). So sánh ví dụ (meta ^String 'x) với (meta ' ^String x) để xem hiệu ứng.

Bạn cần đặt gợi ý lên biểu tượng.

(defmacro def-string 
    [name value] 
    `(def ~(vary-meta name assoc :tag `String) ~value)) 

Và việc sử dụng:

user=> (def-string foo "bar") 
#'user/foo 
user=> (meta #'foo) 
{:ns #<Namespace user>, :name foo, :file "NO_SOURCE_PATH", :line 5, :tag java.lang.String} 
+1

Ahh! Tất nhiên, macro đọc được đánh giá trước defmacros. Cảm ơn. – Ralph

5

Metadata không hiển thị trong một macroexpand vì nó là nghĩa vụ phải được "vô hình".

Nếu macro đúng (không đúng) bạn có thể gọi (meta # 'db-host-option) để kiểm tra dữ liệu meta trên var.

Lưu ý rằng (def sym ...) chèn siêu dữ liệu vào var mà nó nhận được từ biểu tượng. Nhưng^Tag ~ name đặt dữ liệu meta trên ~ name (tên unquote), chứ không phải trên biểu tượng được truyền vào liên kết với tên. Nó không thể làm bất cứ điều gì khác kể từ^Tag ... xử lý được thực hiện bởi người đọc, mà đã được hoàn thành khi mở rộng macro bắt đầu.

Bạn muốn một cái gì đó giống như

(defmacro def-string [name value] 
    `(def ~(with-meta name {:tag String}) ~value)) 


user> (def-string bar 1) 
#'user/bar 
user> (meta #'bar) 
{:ns #<Namespace user>, :name bar, :file "NO_SOURCE_FILE", :line 1, :tag java.lang.String} 
Các vấn đề liên quan