Clojure defmacro cannot be cast to clojure.lang.IFn

defmacro 和 defn有着微妙的区别, 不注意的话会出现难以理解的错误. 下面的的macro只是简单的将参数中的每个元素转换成字符串.

 
(defmacro foo [form]
  (map #(str %) form)
)
 
(foo [ALL hello world 3 abc whatever])
 

抛出错误

 
ClassCastException java.lang.String cannot be cast to clojure.lang.IFn  user/eval11560 (init.clj:9)
 

原因是我将defmacro当成defn一样了, 对于defmacro, 任何其返回的东西会被当成Clojure代码, 并交给eval函数运行, 这里map的返回值是一个字符串列表

 
=>(macroexpand '(foo [ALL hello world 3 abc whatever]))
("ALL" "hello" "world" "3" "abc" "whatever")
 

那么列表的第一个元素"ALL"会被当成函数名, 然而他并不是函数, 因此会报错.

如果要查看转换后的内容, 像上面用macroexpand或者打印

 
(defmacro foo [form]
  (pprint(map #(str %) form))
)