Find which protocols are implemented by a Clojure datatype.

January 13, 2011 at 9:26 pm | Posted in Clojure, Programming | 3 Comments

As 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

  1. interesting investigation, maybe this post could interests you in your investigations: http://sbtourist.blogspot.com/2010/09/dynamic-mixins-in-clojure-experiment.html

  2. @jneira: thanks. I already stumbled upon that blog when investigating dynamic mixins in Clojure and it shows a very nice approach.

  3. […] 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. […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.
Entries and comments feeds.

%d bloggers like this: