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

+6 votes
in Errors by
Describing the user-facing problem:

When an exception is thrown and there is a multimethod in the stacktrace, the name of the multimethod is not shown in the callstack.

When I execute this code:


(defmulti dispatcher (fn [item] (:type item)))

(defmethod dispatcher "cat"
  [{:keys [lives]}]
  (/ lives 0))

(dispatcher {:type "cat" :lives 9})
*e


I get the following stack trace:


[clojure.lang.Numbers divide "Numbers.java" 163]
[clojure.lang.Numbers divide "Numbers.java" 3813]
[config_compilation.core$eval20467$fn__20469 invoke "/Users/marc/dev/circleci/orb-registry/server/src/config_compilation/core.clj" 198]
[clojure.lang.MultiFn invoke "MultiFn.java" 229]
... snip ...


The function name is {{config_compilation.core$eval20467$fn__20469}}.

If I use {{defn}} to declare {{dispatcher}} directly, I get the following stacktrace:


(defn dispatcher
  [{:keys [lives]}]
  (/ lives 0))

(dispatcher {:type "cat" :lives 9})
*e



[clojure.lang.Numbers divide "Numbers.java" 163]
[clojure.lang.Numbers divide "Numbers.java" 3813]
[config_compilation.core$dispatcher invokeStatic "/Users/marc/dev/circleci/orb-registry/server/src/config_compilation/core.clj" 202]
[config_compilation.core$dispatcher invoke "/Users/marc/dev/circleci/orb-registry/server/src/config_compilation/core.clj" 200]
... snip ...


The function name is given correctly as {{config_compilation.core$dispatcher}}.

I'm guessing that the issue could be that the {{defmulti}} macro not setting the {{:name}} metadata, or perhaps not passing the {{:name}} metadata to the compiler correctly.

4 Answers

0 votes
by
_Comment made by: alexmiller_

So, it's kind of interesting if you look into the details of this. Technically, at the point where the exception occurs, the defmulti is not on the stack (that's the dispatch function, which has already completed), but the defmethod is.

defmethods are actually defined as fn's and are typically treated as anonymous fns, although it's actually possible to name them independently:


user=> (defmulti dispatcher (fn [item] (:type item)))
#'user/dispatcher
user=> (defmethod dispatcher "cat" cat-method
         [{:keys [lives]}]
         (/ lives 0))
#object[clojure.lang.MultiFn 0x6e1d4137 "clojure.lang.MultiFn@6e1d4137"]
user=> (dispatcher {:type "cat" :lives 9})
ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:163)
user=> (pst *e 5)
ArithmeticException Divide by zero
    clojure.lang.Numbers.divide (Numbers.java:163)
    clojure.lang.Numbers.divide (Numbers.java:3813)
    user/eval155/cat-method--157 (NO_SOURCE_FILE:11)         ;; <== cat-method
    clojure.lang.MultiFn.invoke (MultiFn.java:229)
    user/eval161 (NO_SOURCE_FILE:12)


I think what would be best is if the defmethod function defaulted to a unique name based on the defmulti function name (if no name was specified).
0 votes
by

Comment made by: alexmiller

CLJ-2212 is related

0 votes
by

Comment made by: marc

Oh, interesting.

The symbol that identifies the multimethod (dispatcher) in this case appended with some sort of string representation of the dispatch value would be a really nice default value here.

So you might have functions named
dispatcher-default, dispatcher-cat, etc.

I assume that you might have to add some additional uniqueness suffix on the end, too.

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