Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolution of Clojure protocol call when class implements several interfaces

Tags:

clojure

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
like image 853
DanLebrero Avatar asked Sep 22 '12 16:09

DanLebrero


1 Answers

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)))))))
like image 168
Abhinav Sarkar Avatar answered Oct 12 '22 08:10

Abhinav Sarkar