当宏调用另一个宏时,Clojure宏和默认参数

Plu*_*tra 2 macros clojure

当定义默认参数时,我遇到一对Clojure宏的问题.

在以下情况下有2个宏,其中mm02调用mm01:

(defmacro mm01
  [ & [ { :keys [ f1 ] :or { f1 long } :as opts } ]]
  `(let []
    (println "(2) ~f1" ~f1)))

(defmacro mm02
  [ & [ { :keys [ f1 ] :as opts } ]]
  `(let []
     (println "(1) ~f1" ~f1)
     (mm01 ~@opts)))
Run Code Online (Sandbox Code Playgroud)

评价:

(mm02 { :f1 byte })
Run Code Online (Sandbox Code Playgroud)

打印出来:

(1) ~f1 #function[clojure.core/byte]
(2) ~f1 #function[clojure.core/long]
Run Code Online (Sandbox Code Playgroud)

但是,我原以为:

(1) ~f1 #function[clojure.core/byte]
(2) ~f1 #function[clojure.core/byte]
Run Code Online (Sandbox Code Playgroud)

我做错了什么或者我错过了什么?

顺便说一句,评价:

(mm01 { :f1 byte })
Run Code Online (Sandbox Code Playgroud)

打印出来:

(2) ~f1 #function[clojure.core/byte]
Run Code Online (Sandbox Code Playgroud)

非常感谢你.

ama*_*loy 5

~@在语法引用上下文中,将一系列事物扩展为几个单独的事物.您的opts绑定是一个映射,它在概念上是一系列映射条目.您可以通过在repl中使用宏将生成的表达式来查看此操作:这通常是查看宏的中间步骤的有用方法,与通过宏上的反复试验进行调试相比较整个.

user=> (let [opts {:f1 'long}]
  #_=>   `(foo ~@opts))
(user/foo [:f1 long])
Run Code Online (Sandbox Code Playgroud)

看到周围的方括号:f1 long?这就是问题:你的另一个宏期望用地图调用,而不是向量.结果,解构无法找到您正在寻找的密钥.要解决此问题,只需删除@并使用普通的unquote,而不是拼接取消引用.

user=> (let [opts {:f1 'long}]
  #_=>   `(foo ~opts))
(user/foo {:f1 long})
Run Code Online (Sandbox Code Playgroud)

作为一项额外的改进,你应该[& [{...}]]用just 替换令人分心的参数节[{...}].它们的行为相同,只是前者允许调用者传递零参数(填入nils)或任何数量的额外参数,这些都被忽略.你的版本是非常轻微的来电者更方便,如果他们意味着省略参数,并得到默认,但不可避免地将导致他们调试一个大麻烦,如果他们离开了争论,或提供太多,不小心.