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
selected by
 
Best answer

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