Pure Danger Tech


Clojure vs Java, pt. 1?

18 Apr 2011

This is not a grand post but rather a small example of Clojure vs Java I ran across. Maybe the first in a longer series…

A while back some of my colleagues at Revelytix built a core library for representing and working RDF data and graphs thereof. One small aspect of that was to define Clojure prototypes that represent the three core RDF types: IRIs, literals, and blank nodes. It’s not particularly important for you to understand what those are but here are those definitions:

(defprotocol RDF_IRI
  (get-iri [iri]))

(defprotocol RDF_BNode
  (get-id [bnode]
  (get-label [bnode]))

(defprotocol RDF_Literal
  (get-lexical [lit])
  (get-datatype [lit])
  (get-language [lit]))

We can extend Clojure protocols over any Java or Clojure type, regardless of whether we control that type. For RDF_IRI, there happens to be a decent Java class that already represents URIs (IRIs are the international form of a URI), namely java.net.URI.

URI doesn’t cover all of the cases but it covers most of the ones I care about at the moment, so we can extend RDF_IRI to URI:

(extend-protocol RDF_IRI
  (get-iri [uri] uri))

This just says to extend the RDF_IRI protocol to the URI type and then implements the get-iri method by returning itself.

If we finally get a customer in the future that requires full IRIs, it’s no problem to extend to some other library’s IRI implementation, or even one that’s subsequently added to Java or our own.

So today, I happened to be hacking on some Java code that needed to virtually the exact same thing I’m doing above. I want to define some interfaces to represent an IRI, a literal, and a blank node. Easy to do of course but what do I do about IRI? I’d really like to define the Java equivalent of the RDF_IRI protocol above with an interface that returns an IRI. And I’d really love to have java.net.URI implement my interface.

What?!? We can’t do that of course. Java doesn’t let me retroactively say that a type defined by someone else implements my interface! So my choices in Java are to implement a wrapper class around URI that implements my custom interface IRI or to implement my own URI class that duplicates the logic in java.net.URI and implements my custom interface. Those choices both suck. Gimme my Clojure protocols.

Another case where this particular example is nice is that it’s easy to extend the RDF_Literal protocol over the existing Java/Clojure types. If I want java.lang.String to implement RDF_Literal, that’s no problem:

(extend-protocol RDF_Literal
  (get-lexical [s] s)
  (get-datatype [s] XSD-STRING)   ;; XSD-STRING is a uri for xsd string constant
  (get-language [s]))

I don’t need to create a type here that holds just a String and implements my new custom interface (RDF_Literal). Instead, I just extend RDF_Literal to String. If I need fancier types (I do in some cases), then I create my own type for those and extend the protocol over them.

Clojure protocols here let me say exactly what I want to say with no fuss. Java actually prevents me from saying it at all and forces me to create alternate intermediaries that are unnecessary. If you’re feeling design patterny, you’d call them adapters. But isn’t it better to avoid the adapter altogether?