多重arity在defmacro的clojure [英] multiple arity in defmacro of clojure

查看:128
本文介绍了多重arity在defmacro的clojure的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个与Clojure中的defmacro相关的奇怪问题,我有像

 (defmacro ttt 
[](ttt 1))
([a](ttt a 2))
([ab](ttt ab 3))
([abc]`(println〜a〜b 〜c)))

而且我运行(ttt),它假设变成(println 1 2 3),并打印1 2 3,但是我得到的是

  ArityException传递给:t1的$ args(-1)的数量错误tt clojure.lang.Compiler.macroexpand1(Compiler.java:6473)

经过一番调查,我明白我应该写

 (defmacro ttt 
([]`(ttt 1))
([a]`(ttt〜a 2))
〜a〜b 3))
([abc]`(println〜a〜b〜c)))


b $ b

但为什么第一个版本失败?和 args 太奇怪了, -1 来自?



宏有两个隐藏的参数& form / code>和& env ,提供有关高级使用的调用和绑定的其他信息。如果你要有一个宏调用本身的不同的arity版本,你必须自己传递这些参数。

 (defmacro baz 
([](baz& form& env 1))
[a](baz& form& env a 2))
([ab](baz& form& env ab 3))
([abc]`(println〜a〜b 〜c)))

user => (bigexpand-1'(baz))
(clojure.core / println 1 2 3)

user => (baz)
1 2 3
nil



Arity异常消息从计数的参数



得到(-1)arity异常的原因是因为在生成一般宏使用的错误消息时,编译器会减去这两个隐藏的参数。 ttt 的真正消息是args(1)的错误数,因为你提供了一个参数 a



在野外不常见的多元宏



在实践中,我建议完全避免多元宏。相反,考虑一个帮助函数来代表宏来做大部分工作。事实上,这对于其他宏也是一个很好的做法。

 (defn- bar 
bar 1))
([a](bar a 2))
([ab](bar ab 3))
(printcn〜 )


(defmacro foo [& args](apply bar args))

user => (macroexpand-1'(foo))
(clojure.core / println 1 2 3)

user => (foo)
1 2 3
nil



宏扩展是递归的< h2>

您的第二个 ttt 版本也适用于宏展开的递归性质

  user => (macroexpand-1'(ttt))
(user / ttt 1)
user => (macroexpand-1 * 1)
(user / ttt 1 2)
user => (macroexpand-1 * 1)
(usr / ttt 1 2 3)
user => (macroexpand-1 * 1)
(clojure.core / println 1 2 3)

  user => (macroexpand'(ttt))
(clojure.core / println 1 2 3)


I encountered a strange problem relating to defmacro in Clojure, I have code like

(defmacro ttt
  ([] (ttt 1))
  ([a] (ttt a 2))
  ([a b] (ttt a b 3))
  ([a b c] `(println ~a ~b ~c)))

and I run with (ttt), it suppose to become (println 1 2 3), and print "1 2 3", but what I got is

ArityException Wrong number of args (-1) passed to: t1$ttt clojure.lang.Compiler.macroexpand1 (Compiler.java:6473)

after some investigation, I understand I should write

(defmacro ttt
  ([] `(ttt 1))
  ([a] `(ttt ~a 2))
  ([a b] `(ttt ~a ~b 3))
  ([a b c] `(println ~a ~b ~c)))

but why the first version failed? and args is too strange to understand, where -1 comes from?

解决方案

Macros have two hidden arguments

Macros have two hidden arguments &form and &env that provide additional information about invocation and bindings for advanced use. If you are going to have a macro call different arity version of itself, you must pass along these arguments yourself.

(defmacro baz
  ([] (baz &form &env 1))
  ([a] (baz &form &env a 2))
  ([a b] (baz &form &env a b 3))
  ([a b c] `(println ~a ~b ~c)))

user=> (macroexpand-1 '(baz))
(clojure.core/println 1 2 3)

user=> (baz)
1 2 3
nil

Arity exception messages subtract the hidden arguments from the count

The reason you get the (-1) arity exception is because the compiler subtracts these two hidden arguments when generating the error message for the general macro usage. The true message here for your first version of ttt would be "Wrong number of args (1)" because you supplied one argument a but the two additional hidden arguments were not provided by self-invocation.

Multi-arity macros not common in the wild

In practice, I suggest avoiding multi-arity macros altogether. Instead, consider a helper function to do most of the work on behalf of the macro. Indeed, this is often a good practice for other macros as well.

(defn- bar
  ([] (bar 1))
  ([a] (bar a 2))
  ([a b] (bar a b 3))
  ([a b c] `(println ~a ~b ~c)))


(defmacro foo [& args] (apply bar args))

user=> (macroexpand-1 '(foo))
(clojure.core/println 1 2 3)

user=> (foo)
1 2 3
nil

Macro expansion is recursive

Your second ttt version works as well due to the recursive nature of macro-expansion

user=> (macroexpand-1 '(ttt))
(user/ttt 1)
user=> (macroexpand-1 *1)
(user/ttt 1 2)
user=> (macroexpand-1 *1)
(usr/ttt 1 2 3)
user=> (macroexpand-1 *1)
(clojure.core/println 1 2 3)

So,

user=> (macroexpand '(ttt))
(clojure.core/println 1 2 3)

这篇关于多重arity在defmacro的clojure的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆