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

0 votes
in Errors by

09:43 $ clj
Clojure 1.8.0
(defmulti foo :bar)
(defmethod foo :qix [quux znoot] (println 'hi))
#'user/foo
user=> #object[clojure.lang.MultiFn 0x205d38da "clojure.lang.MultiFn@205d38da"]
user=> (foo {:bar :qix})
ArityException Wrong number of args (1) passed to: user/eval5/fn--6  clojure.lang.AFn.throwArity (AFn.java:429)
user=>


It is an implementation detail that multi methods are implemented via anonymous functions. I would expect the error message to at least contain the name of the function that failed, in this case it should have been something like


ArityException Wrong number of args (1) passed to: user/eval5/foo--6  clojure.lang.AFn.throwArity (AFn.java:429)


Several approaches can be taken here:

The first (and simplest) is to change the definition of {{defmethod}} so that the anonymous function gets a name.
This leads to an error message like:

user=> (defmulti foo :bar)
(defmethod foo :qix [quux znoot] (println 'hi))
(foo {:bar :qix})#'user/foo
user=> #object[clojure.lang.MultiFn 0x4e928fbf "clojure.lang.MultiFn@4e928fbf"]
user=>
ArityException Wrong number of args (1) passed to: user/eval5/foo--6  clojure.lang.AFn.throwArity (AFn.java:429)
user=>

In addition to this, one could modify Compiler.java to look for the calls to "addMethod"

String prefix = "eval";
if (RT.count(form) > 2) {
   Object third = RT.nth(form, 2);
   if (third != null &&
       "clojure.core/addMethod".equals(third.toString()))
      prefix = "multi_fn";
}
ObjExpr fexpr = (ObjExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form), prefix + RT.nextID());

which would give us error messages like

ArityException Wrong number of args (1) passed to: user/multi-fn5/foo--6  clojure.lang.AFn.throwArity (AFn.java:441)

2 Answers

0 votes
by

Comment made by: alexmiller

Interestingly, it is actually possible to give defmethods names that get included in their class names. The arglist for defmethod is: ((link: multifn dispatch-val & fn-tail)) where fn-tail is the tail arguments as passed to fn, which includes an optional name.

So you can do this:

`
(defmulti foo class)
;; create defmethod with a name HI:
(defmethod foo String HI [x] (throw (Exception. (str "hi " x))))

;; Invoke:
(foo "bleh")
Exception hi bleh user/eval1248/HI--1249 (form-init1348837216879020682.clj:1)
`

Note the HI in the class name. This doesn't address everything above but it is a helpful debugging trick.

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2263 (reported by slipset)
...