Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Compiler by

The Clojure compiler appears to use the :arglists metadata of vars when compiling function invocations, in a way which conflicts with functions being called recursively in the body of their defn form with dynamically specified metadata.
A minimal example:

(defn foo
  {:arglists (list '[n])}
  [n]
  (if (even? n)
    n
    (foo (dec n))))

Which results in the compilation error:

Syntax error (ClassCastException) compiling foo at (REPL:6:5). class clojure.lang.Symbol cannot be cast to class clojure.lang.IPersistentVector (clojure.lang.Symbol and clojure.lang.IPersistentVector are in unnamed module of loader 'app')

The compiler appears to be using the unevaluated form of the arglist (list (quote [n])) when parsing the call to foo, throwing an exception due to the symbol list not being the expected argument vector.

Here's a slightly more realistic example of where this could occur:

(def ^:private shape-attrs
  '{:keys [height width color]})

(defn draw
  {:arglists (list ['shape]
                   ['shape shape-attrs])}
  ([shape]
   (draw shape {:color "black"}))
  ([shape attrs]
   :ok))

1 Answer

0 votes
by

Var arglists should be a literal list of vectors, like:

:arglists '([shape] [shape shape-attrs])
by
I couldn't find this restriction mentioned anywhere, is it the case then that any programmatically defined arglists *must* be specified via `alter-meta!` after the var's creation?

Note that the following compiles and works just fine:
```
(defn draw
  {:arglists (list ['shape shape-attrs])}
  ([shape attrs]
   :ok))
```

the error only arises when a recursive call to the var is made within its own body.
by
I don't know that there is much or any documentation on :arglists, but I thinkyou can assume that the :arglists should be data in a defn.
by
A quick Github code search shows a few instances of this out in the wild, including a core Clojure lib:

https://github.com/clojure/data.xml/blob/e6522752bd9d66b0d6c1255f1a706433975a92cc/src/main/clojure/clojure/data/xml.clj#L135

Without explicit documentation or compiler warnings that only literal quoted lists were allowed in that particular metadata slot, I feel that it would be quite natural for people to assume otherwise.
by
...