2013-02-28 35 views
1

我有以下的Clojure代码:为什么我的宏的表单在函数调用之前未被评估?

(defn mul [a b] 
    (* a b)) 

(defmacro create-my-macro [macroname] 
    `(defmacro ~macroname [a# b#] 
     (mul a# b#))) 

(create-my-macro my-mul) 

(my-mul 1 2) 
;; => 2 
(my-mul (+ 1 1) 2) 
;; => ClassCastException clojure.lang.PersistentList cannot be cast to java.lang.Number 

我得到了我想要当我eval包裹论点的mul调用的答案:

(mul (eval #a) (eval #b)) 

但我不明白为什么它有必要这样做:如果my-mul宏已经被直接定义(而不是通过另一个宏),它就会起作用。例如,以下工作正常:

(defmacro my-mul [a b] `(mul ~a ~b)) 
(my-mul (+ 1 1) 2) 
;; => 4 

为什么我看到这种行为?


编辑:回应评论,下面是发生故障的情况下,macroexpands(即不使用eval):

(macroexpand '(create-my-macro my-mul)) 
;; => (do 
;;  (clojure.core/defn my-mul 
;;  ([&form &env a__58__auto__ b__59__auto__] 
;;   (foo/mul a__58__auto__ b__59__auto__))) 
;;  (. (var my-mul) (setMacro)) (var my-mul)) 

(macroexpand '(my-mul (+ 1 1) 2)) 
;; => ClassCastException clojure.lang.PersistentList cannot be cast to java.lang.Number clojure.lang.Numbers.multiply (Numbers.java:146) 
+0

调试提示:这是什么macroexpand说明了什么? – 2013-02-28 08:16:00

+0

刚刚添加他们 – 2013-02-28 08:39:44

回答

3

正如你展示,你的代码'喜欢发射看起来像:

(defmacro my-mul [a b] `(mul ~a ~b)) 

所以你需要语法引用我T和后缀以#所有当地人:

`(defmacro my-mul [a# b#] `(mul ~a# ~b#)) 

因此,你的宏观发射宏应该是:

(defmacro create-my-macro [macroname] 
    `(defmacro ~macroname [a# b#] 
     `(mul ~a# ~b#))) 
+0

是的,你的回答比我的更详细:) – hsestupin 2013-02-28 09:16:26

2
(defmacro create-my-macro [macroname] 
`(defmacro ~macroname [a# b#] 
    `(mul ~a# ~b#))) 
相关问题