Find which protocols are implemented by a Clojure datatype.
January 13, 2011 at 9:26 pm | Posted in Clojure, Programming | 3 CommentsAs part of a somewhat larger experiment (implementing dynamic mixins in Clojure) I needed to know what protocols are implemented for a given object (defined for example by defrecord or deftype). For example I have defined the next protocol:
(ns dci.core) (defprotocol LogProtocol (log [x] "log method"))
And lets suppose that I have defined the following type:
(defrecord Foo [f1 f2] LogProtocol (log [this] (println (:f1 this) (:f2 this))))
When I now tried:
(extenders LogProtocol)
I was expecting to see Foo. What I got instead was nil.
However when I did the following:
(defrecord Bar [b1 b2]) (extend Bar LogProtocol {:log (fn [this] (println (:b1 this) (:b2 this)))}) (extenders LogProtocol)
I get (dci.core.Bar) as expected.
When I try to find the implemented protocol by using the ancestors function, I get the opposite result:
(ancestors Foo)
has dci.core.LogProtocol in it’s list of ancestors, while
(ancestors Bar)
doesn’t.
So there is a difference (at least in Clojure 1.2) in behavior whether you implement the protocol directly with defrecord (as done with Foo) or extend it later (Bar). I wanted a solution that works with both declarations. I first implemented a function protocol? that checks for a given symbol if it is a protocol:
(defn protocol? [maybe-p] (boolean (:on-interface maybe-p)))
This implementation depends on the :on-interface key, which is undocumented. Next I created a function all-protocols that returns a list of all protocols in the current namespace (*ns*).
(defn all-protocols [] (filter #(protocol? @(val %)) (ns-publics *ns*)))
And finally I wrote implemented-protocols to filter all protocols that are available for a given object:
(defn implemented-protocols [sym] (filter #(satisfies? @(val %) sym) (all-protocols)))
The complete code with some tests:
(ns dci.core) (defn protocol? [maybe-p] (boolean (:on-interface maybe-p))) (defn all-protocols [] (filter #(protocol? @(val %)) (ns-publics *ns*))) (defn implemented-protocols [sym] (filter #(satisfies? @(val %) sym) (all-protocols))) ; test protocol and records (defprotocol LogProtocol (log [x] "log method")) (defrecord Foo [f1 f2] LogProtocol (log [this] (println (:f1 this) (:f2 this)))) (defrecord Bar [b1 b2]) (extend Bar LogProtocol {:log (fn [this] (println (:b1 this) (:b2 this)))}) (println (implemented-protocols (Foo. 1 2))) ; will output ([LogProtocol #'dci.core/LogProtocol]) (println (implemented-protocols (Bar. 3 4))) ; will output ([LogProtocol #'dci.core/LogProtocol])
In a next blog I will tell a bit more about my dynamic mixins implementation in Clojure.
3 Comments »
RSS feed for comments on this post. TrackBack URI
Leave a Reply
Blog at WordPress.com.
Entries and comments feeds.
interesting investigation, maybe this post could interests you in your investigations: http://sbtourist.blogspot.com/2010/09/dynamic-mixins-in-clojure-experiment.html
Comment by jneira— January 13, 2011 #
@jneira: thanks. I already stumbled upon that blog when investigating dynamic mixins in Clojure and it shows a very nice approach.
Comment by maurits— January 13, 2011 #
[…] This post was mentioned on Twitter by 23号, Maurits Rijk and others. Maurits Rijk said: My first blog this year. About #Clojure protocols: http://bit.ly/fJN6sy. […]
Pingback by Tweets that mention Find which protocols are implemented by a Clojure datatype. « Maurits thinks aloud -- Topsy.com— January 14, 2011 #