DanLebrero
DanLebrero

Reputation: 8593

Resolution of Clojure protocol call when class implements several interfaces

Given a set, map and vector in Clojure implement both IPersistentCollection and IFn, how does Clojure decide which implementation of SayHi to use:

(defprotocol SayHi
  (hi [this]))

(extend-protocol SayHi
  clojure.lang.IPersistentCollection
  (hi [_] (println "Hi from collection"))
  clojure.lang.IFn
  (hi [_] (println "Hi from Fn!"))
  clojure.lang.IPersistentSet
  (hi [_] (println "Hi from set!")))

(hi #{})
Hi from set!
(hi [])
Hi from collection

Upvotes: 5

Views: 350

Answers (1)

Abhinav Sarkar
Abhinav Sarkar

Reputation: 23802

Protocol dispatch is done on the type of the first argument of the function. When multiple implementations match the type of the first argument, the most specific implementation is chosen. That is why the (hi #{}) call resolves to the set implementation and not the collection or fn implementations even though a set (#{}) implements both of those.

The find-protocol-impl function in the clojure-deftype.clj seems to handle the protocol to implementing object resolution:

(defn find-protocol-impl [protocol x]
  (if (instance? (:on-interface protocol) x)
    x
    (let [c (class x)
          impl #(get (:impls protocol) %)]
      (or (impl c)
          (and c (or (first (remove nil? (map impl (butlast (super-chain c)))))
                     (when-let [t (reduce1 pref (filter impl (disj (supers c) Object)))]
                       (impl t))
                     (impl Object)))))))

Upvotes: 5

Related Questions