2012-09-25 33 views
5

Điều này tương tự như vấn đề được thảo luận trong Treat Clojure macro as a function nhưng khi thử cách tiếp cận trong câu trả lời hàng đầu, tôi gặp lỗi. Hy vọng quá nhiều thông tin về ứng dụng cụ thể của tôi là không cần thiết, bởi vì nó là khá phức tạp, nhưng đây là một phiên bản cất những gì tôi đã cố gắng để làm:Macro clojure là chức năng/'Một phần' cho Macros?

(defmacro make-fn [m arg1] 
    `(fn [& args#] 
     (eval `(~'~m ~'~arg1 [email protected]#)))) 

Tôi đã sử dụng macro trong bối cảnh này:

(let [columns (make-columns table-width) 
     table-name (keyword (str "table_" n))] 
    (apply (make-fn helpers/tbl table-name) columns)) 

"người trợ giúp/tbl" là macro dự kiến ​​một từ khóa tên bảng và một số biến danh sách chứa thông số cột (như [: varchar 100] hoặc một cái gì đó). Tôi đang cố gắng để tạo ra các thông số kỹ thuật bảng cơ sở dữ liệu ngẫu nhiên trên bay để tạo thuận lợi cho một số thử nghiệm. Dù sao, khi cố gắng thực thi mã trên, tôi nhận được lỗi sau:

CompilerException java.lang.RuntimeException: Unable to resolve symbol: table-name in this context, compiling:(NO_SOURCE_PATH:1) 

tôi loại nắm bắt các vấn đề: mở rộng vĩ mô được thực hiện tại thời gian biên dịch, và tôi đang cố gắng để bao gồm một giá trị thời gian chạy trong macro mở rộng, do đó việc sử dụng lẻ trích dẫn và unquoting để có được tất cả mọi thứ thiết lập vừa phải. Về cơ bản, tôi muốn một phần cho các macro và tôi cần có khả năng sử dụng lại cơ chế này cho các macro khác nhau trong các không gian tên khác nhau và có tất cả độ phân giải biến xuất hiện đúng. Điều này thậm chí có thể?

Trả lời

2

Vấn đề là do cách Clojure giải quyết các ký hiệu trong biểu thức cú pháp trích dẫn (backtick). Để tránh việc bắt giữ biến cố ý, Clojure luôn diễn giải các ký hiệu trong biểu thức trích dẫn cú pháp như đề cập đến Vars (không phải là người dân địa phương).

Bạn có thể giải quyết vấn đề này bằng cách "tạo mã tạo mẫu" của riêng bạn, tương đương với mã được tạo bởi cú pháp-trích dẫn. Nó như xấu xí như tội lỗi, nhưng nó hoạt động ... chỉ cần không nói tôi không cảnh báo bạn:

(defmacro make-fn [m arg1] 
    (let [g (gensym)] 
    (list 'fn ['& g] 
     (list 'eval (list 'concat (list 'list m arg1) g))))) 

Wow, điều này cũng giống như một hồi tưởng lại ngày Common Lisp tôi ...

+0

BTW , thực tế thú vị về 'cú pháp trích dẫn': nó được thực hiện hoàn toàn trong trình đọc. Khi người đọc thấy một backtick, nó đọc dạng sau, sau đó biến nó đệ quy thành mã sử dụng 'seq',' concat' và 'list'. Trình biên dịch chỉ thấy mã 'concat' /' list' kết quả. –

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