0

I am trying to write a macro that redirects a call to the function that does it. It is a way to collect all published functions into a top-level clj file. https://martinfowler.com/bliki/PublishedInterface.html

I want to copy the doc string and arglists, and the docstring works fine, but not the argslists. What am I missing?

(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)

If I use the commented line instead, I get

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

This is just a sample where the namespace is hardcoded to clojure core.

This question is really similar, but you see I have no problem copying the :doc part, what is to special about :arglists

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

mattias
  • 870
  • 5
  • 18

1 Answers1

0

You are getting that error because arglists are essentially a list and splicing it with ~ causes this list to evaluate.

Let me explain with an example:

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

When you try to do ~(with-meta fname {:doc doc :arglists arglists}) within your macro you are literally evaluating form that is bound to arglists symbol. So in case of inc you are essentially trying to do this:

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

which reports the error you are getting.

To avoid this you need to prevent arglists form from evaluation. One of the ways to achieve this is to put it within a quote call:

(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)))
OlegTheCat
  • 4,443
  • 16
  • 24