Clojure's protocols were a feature I'd not used much of until I started working on a little pet project: clj-hector (a library for accessing the Cassandra datastore).
Protocols provide a means for attaching type-based polymorphic functions to types. In that regard they're somewhat similar to Java's interfaces (indeed Java can interoperate with Clojure through the protocol-generated types although it's not something I've tried). Here's the example of their use from the Clojure website:
(defprotocol P | |
(foo [x]) | |
(bar-me [x] [x y])) | |
(deftype Foo [a b c] | |
P | |
(foo [x] a) | |
(bar-me [x] b) | |
(bar-me [x y] (+ c y))) | |
user> (bar-me (Foo. 1 2 3) 42) | |
45 |
Protocols go deeper though: they can be used with already defined types (think attaching behaviour through mixing modules into objects with Ruby's include). It's for this reason that they've been a particularly nice abstraction for building adapters upon: glue that translates Java objects to Clojure structures when doing interop.
Here's a snippet from a bit of the code that converts Hector query results into maps:
(defprotocol ToClojure | |
(to-clojure [x] "Convert hector types to Clojure data structures")) | |
(extend-protocol ToClojure | |
RowsImpl | |
(to-clojure [s] | |
(map to-clojure (iterator-seq (.iterator s)))) | |
RowImpl | |
(to-clojure [s] | |
{:key (.getKey s) | |
:columns (to-clojure (.getColumnSlice s))}) | |
QueryResultImpl | |
(to-clojure [s] | |
(with-meta (to-clojure (.get s)) {:exec_us (.getExecutionTimeMicro s) | |
:host (.getHostUsed s)}))) |
QueryResultImpl
contains results for queries. In one instance, this is RowsImpl
which contains Row
results (which in turn are converted by other parts of the extend-protocol
statement in the real code). The really cool part of this is that dispatch happens polymorphically: there's no need to write code to check types, dispatch (and call recursively down the tree). Instead, you map declaratively by type and build a set of statements about how to convert. Clojure does the rest.
Pretty neat.