Given some type or record, how can I get all the protocols it implements?
Let's say we have the following code:
(defprotocol P1
(op1 [this]))
(defprotocol P2
(op2 [this]))
(defrecord R []
P1
(op1 [_] 1)
P2
(op2 [_] 2))
And what I need is a function that does something like this:
(all-protocols-for-type R) ;; => (P1 P2)
It will be good if there's something backend-agnosting, because I'd like to have a mechanism for both Clojure and ClojureScript.
UPD: the intention for this is to introspect what functionality the particular type provides. Let's say, by doing this:
user> (supers (type {}))
#{clojure.lang.AFn clojure.lang.ILookup java.lang.Object java.util.Map clojure.lang.Seqable java.lang.Runnable clojure.lang.IPersistentCollection java.io.Serializable clojure.lang.IFn clojure.lang.APersistentMap clojure.lang.Associative java.util.concurrent.Callable clojure.lang.IKVReduce clojure.lang.Counted clojure.lang.IMeta clojure.lang.IMapIterable java.lang.Iterable clojure.lang.IPersistentMap clojure.lang.IEditableCollection clojure.lang.IObj clojure.lang.MapEquivalence clojure.lang.IHashEq}
I will know that I can use map as a function (IFn
), lookup a value by key there (Associative
) and even do reduce-kv
on it (IKVReduce
).
This is the closest you can get in Clojure without doing something crazy:
(require '[clojure.set :as set])
(deftype EmptyType [])
(defrecord EmptyRecord [])
(defn all-protocols-for-type [t]
(map (comp symbol (memfn getSimpleName))
(set/difference (supers t)
(set/union (supers EmptyType)
(supers EmptyRecord)))))
I used that specific map
just to get an output with the same format you gave in your question; you should modify the mapping function as you see fit.
Basically, what you're asking is the other side of the coin to this. Extension of a protocol using deftype
or defrecord
stores the information on the type, and extension using extend-type
or extend-protocol
stores the information on the protocol.
So if all your extensions are done directly in deftype
or defrecord
, you can get all the protocols for a given type using the method I gave above. If all your extensions are done with extend-type
or extend-protocol
, you can get all the types for a given protocol using the method Alex gave in his answer to that other question. But there isn't a nice way to do both in all cases.
This is not possible in general, on the JVM at least, since extending a type to a protocol leaves no information on the type, only on the protocol. In clojurescript I'm not sure. Why do you want this? It's not really very useful to have a list of protocols, if you don't even know what those protocols are for.
And what I need is a function that does something like this:
(all-protocols-for-type R) ;; => (P1 P2)
I can't possibly think of any real world utility of a list of protocols implemented for a type, unless this list will be compared to another list of protocol references.
If the protocol references (in the 2nd list) is known the following would do:
(defn extends-all? [protocols typ]
(reduce (fn [acc p] (and acc (extends? p typ)))
true
protocols))
(extends-all? [P1 P2] R) ;; => true
If the goal isn't to compare against a list of protocols, please update the question with the actual use case.