quoting choices based on ~ and ~@ in Clojure macro

I have two different Clojure macros, but based on the operation (~@ and ~), I need to quote the input or not.

(defmacro make2 [t]
  `(list 1 ~@t)) 

(defmacro make3 [t]
  `(list 1 ~t))

(make2 (1 2 3)) -> (1 1 2 3)

(make3 '(1 2 3)) -> (1 (1 2 3))

Why is this? I can guess that with macro, the arguments are not evaluated (that's the reason why make2 doesn't cause an error). However after getting the arguments, I'm not sure the logic to process them.


(macroexpand-1 '(make2 (1 2 3)))
;; ==> (clojure.core/list 1 1 2 3)

(macroexpand-1 '(make3 (1 2 3)))
;;==> (clojure.core/list 1 (1 2 3))

~@ splices the list (1 2 3) into the expression (list 1 ...) and then evaluates the resulting expression, ie evaluates the function list with the arguments 1 1 2 3 . Clojure evaluates each argument, but numbers evaluate to themselves, of course.

~ just inserts the argument (1 2 3) into (list 1 ...) as its second argument and then evaluates the entire expression, evaluating each argument to list as well. When it does, Clojure sees that there is an unquoted pair of parentheses in the second argument, and assumes that the first expression after that left parenthesis is a function (or macro). That is, Clojure assumes that the inner instance of 1 is a function, which it's not. That's why you get the exception

ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn ...

which says that a Long integer cannot be transformed into a function.

链接地址: http://www.djcxy.com/p/65764.html

上一篇: Clojure中的宏,评估和引用

下一篇: 在Clojure宏中引用基于〜和〜@的选择