当定义默认参数时,我遇到一对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)
非常感谢你.
~@在语法引用上下文中,将一系列事物扩展为几个单独的事物.您的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)或任何数量的额外参数,这些都被忽略.你的版本是非常轻微的来电者更方便,如果他们意味着省略参数,并得到默认,但不可避免地将导致他们调试一个大麻烦,如果他们离开了争论,或提供太多,不小心.
| 归档时间: |
|
| 查看次数: |
117 次 |
| 最近记录: |