2010-06-04 31 views
13

Tôi xác định một vĩ mô unless như sau:Xác định Clojure cú pháp vĩ mô

user=> (defmacro unless [expr body] (list 'if expr nil body)) 
#'user/unless 
user=> (unless (= 1 2) (println "Yo")) 
Yo 

Như bạn có thể nhìn thấy nó hoạt động tốt.

Bây giờ, trong Clojure một danh sách có thể được định nghĩa theo hai cách:

; create a list 
(list 1 2 3) 

; shorter notation 
'(1 2 3) 

Điều này có nghĩa rằng unless vĩ mô có thể được viết mà không cần từ khóa list. Tuy nhiên, điều này dẫn đến một ngoại lệ Java bị ném:

user=> (unless (= 1 2) (println "Yo")) 
java.lang.Exception: Unable to resolve symbol: expr in this context 

Ai đó có thể giải thích tại sao điều này không thành công?

+6

FYI, Clojure đã có các macro tương tự trong lõi, được gọi là 'when-not' và 'if-not'. –

Trả lời

15

'(foo bar baz) không phải là lối tắt cho (list foo bar baz), đó là lối tắt cho (quote (foo bar baz)). Trong khi phiên bản danh sách sẽ trả về một danh sách chứa các giá trị của các biến foo, bar và baz, phiên bản với ' sẽ trả về một danh sách chứa các ký hiệu foo, bar và baz. (Nói cách khác '(if expr nil body) cũng giống như (list 'if 'expr 'nil 'body).

Điều này dẫn đến một lỗi bởi vì với phiên bản được trích dẫn macro sẽ mở rộng để (if expr nil body) thay vì (if (= 1 2) nil (println "Yo")) (vì thay vì thay thế những lập luận của vĩ mô cho expr và cơ thể, nó chỉ trả về tên expr và phần thân (sau đó được xem là các biến không tồn tại trong mã mở rộng). nó cho phép bạn đánh giá một số subexpressions unquoted bằng cách sử dụng ~.Ví dụ macro của bạn có thể được viết lại là (defmacro unless [expr body] `(if ~expr nil ~body)). Điều quan trọng ở đây là exprbody không được bỏ phiếu với ~. Bằng cách này, việc mở rộng sẽ chứa giá trị của chúng thay vì theo nghĩa đen có chứa các tên exprbody.

+0

Hơn nữa '\' 'sẽ chú ý rằng macro là hợp vệ sinh. 'danh sách' sẽ không bao giờ được sử dụng để xác định mở rộng macro. – kotarak

+0

Và một chú thích ngắn cho chính macro: bạn có thể hỗ trợ ngầm 'do' để cho phép nhiều biểu mẫu trong phần thân macro. '(defmacro trừ khi [expr & body] \' (nếu ~ expr nil (do ~ @ body))) ' – kotarak

+1

sử dụng danh sách là tốt nếu hầu hết các biểu mẫu của bạn sẽ bị thoát; '(defmacro my-if [pred else else] \' (nếu ~ pred ~ rồi ~ else)) 'vs' (defmacro my-if [pred then else] (danh sách \ 'nếu pred thì khác))', ví dụ . –

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