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

0 votes
in Multimethods by

It seems (?) like Clojure makes all integers instances of java.lang.Long. I recently found a library I use (which wraps a Java lib) was returning java.lang.Integer instead. This broke a multimethod I have which was dispatching on class.

My solution was to simply make a new defmethod to handle java.lang.Integer. It's exactly like the defmethod for handling java.lang.Long.

Is there a more elegant way to do this? Can a defmethod be made to handle a set of dispatch values? There is a common ancestor class for Integer and Long, java.lang.Number, but I don't want to use that because it will catch floats, which should fall through.

I suppose I could rewrite the dispatch to test if the class of input is Integer or Long and just dispatch those as :int and then dispatch java.lang.String as :string, although that actually may end up being more code :)

Current form is along these lines:

 (defmulti int-or-nil class)

(defmethod int-or-nil java.lang.Long
  [integer]
  (identity integer))

(defmethod int-or-nil java.lang.Integer
  [integer]
  (identity integer))

(defmethod int-or-nil java.lang.String
  [maybe-int-string]
  (try (Integer/parseInt maybe-int-string)
       (catch java.lang.NumberFormatException e)))

(defmethod int-or-nil :default
  [not-int])

2 Answers

+1 vote
by

Clojure lets you write defmulti definitions with any dispatch function you choose, not only class as you have shown in your example.

You could instead choose a dispatch function like this:

(defn my-dispatch-fn [x]
  (if (integer? x)
    :integer
    (class x)))

and then a defmethod with dispatch value :integer will be called for any value x such that (integer? x) returns true.

+1 vote
by

You can give Long and Integer a common ancestor in a custom hierarchy that you tie to the multimethod.

(def h (-> (make-hierarchy)
           (derive Long :num)
           (derive Integer :num)))
(defmulti yy type :hierarchy #'h)
(defmethod yy :num [x] (format "%o is a num" x))
(defmethod yy :default [x] "I don't know")
(yy 7)
(yy (Integer. 42))
...