Clojure records and protocols eschew inheritance. For details see the “Datatypes and protocols are opinionated” section on the Clojure datatypes page.

If you are used to Java style type inheritance you might be surprised that there is no explicit record/protocol mechanism for defining one type as a “sub-type” of another and inheriting the super-type’s implementation. You might even think that Clojure datatypes are less powerful than Java clasess… but you would be wrong.

The trick is that Clojure allows the implementation of a protocol to be specified as a map. Just a simple, standard Clojure map that maps function names to implementations. Since implementations are expressed as maps, the surprising place to look for information on implementation “inheritance” is just the functions that operate on maps. For example: assoc and merge.

Notice in the example that a TrainedDog contains a Dog as a member. This, combined with an implementation of to-dog that returns the TrainedDog’s Dog, allows the functions defined in base-behavior to also operate on TrainedDogs.

Clojure maps are constructed using the full power of the Clojure language. Maps can be combined in ways that emulate Java’s single inheritance model or more complex multiple inheritance models and mixins. Combine maps in any way needed to do what you need.

This is a great example of embracing standard Clojure datatypes as the means of abstraction for a problem (in this case, the problem is “how to specify type implementations”) and gaining the full power of Clojure as a consequence.

(This was presented at the October 2010 Clojure Cljub )