复制:arglists成为一个clojure def

时间:2017-12-01 15:49:52

标签: clojure

我正在尝试编写一个宏,将调用重定向到执行此操作的函数。这是一种将所有已发布函数收集到顶级clj文件中的方法。 https://martinfowler.com/bliki/PublishedInterface.html

我想复制doc字符串和arglists,docstring工作正常,但不是argslists。我错过了什么?

(defmacro idef
  [fname]
  (let [sym (symbol (str "clojure.core/" fname))
        metas (meta (find-var sym))
        arglists (:arglists metas)
        doc (:doc metas)]
    ;;`(def ~(with-meta fname {:doc doc :arglists arglists}))
    `(def ~(with-meta fname {:doc doc})
       ~sym)))

(idef inc)

如果我使用注释行,我会得到

CompilerException clojure.lang.ArityException: Wrong number of args (0) passed to: PersistentVector, compiling:(interface.clj:22:1) 

这只是将命名空间硬编码为clojure核心的示例。

这个问题非常相似,但你看我复制:doc部分没有问题,有什么特别之处:arglists

Help me write a Clojure macro which automatically adds metadata to a function definition

1 个答案:

答案 0 :(得分:0)

您收到该错误,因为arglists本质上是一个列表,并使用~拼接它会导致此列表进行评估。

让我用一个例子来解释:

user> (:arglists (meta #'clojure.core/inc))
([x])

当您尝试在宏中执行~(with-meta fname {:doc doc :arglists arglists})时,您字面评估绑定到arglists符号的表单。因此,在inc的情况下,您实际上是在尝试这样做:

user> ([x])
ArityException Wrong number of args (0) passed to: PersistentVector  clojure.lang.AFn.throwArity (AFn.java:429)

报告您收到的错误。

为避免这种情况,您需要阻止arglists表单进行评估。实现此目标的方法之一是将其置于quote调用中:

(defmacro idef
  [fname]
  (let [sym (symbol (str "clojure.core/" fname))
        metas (meta (find-var sym))
        arglists (:arglists metas)
        doc (:doc metas)]
    `(def ~(with-meta fname {:doc doc :arglists `(quote ~arglists)}) ~sym)))