This post looks at Clojure's polymorphism mechanisms using types. There are essentially two constructs to define new types:
defstruct is a third, but it is recommended to use
defrecord instead of
defstruct . We will take some inspiration by the loom graph library, meaning our examples will be concerned with graphs.
The main difference between the two constructs is that
defrecord supports everything that
clojure.lang.PersistentMap implements whereas
deftype only comes with a constructor and field accessors.
deftype comes with options to make the field mutable. Only use these options when you know what you are doing and avoid it whenever possible.
Both type constructors also generate a functional constructor, which is the type name prefixed by
Records work just like maps.
One can assoc existing fields as well as new ones.
They also support metadata.
A protocol defines a set of named method plus their signatures.
Types can then be extended with a protocol via
There is also a shorthand for specifying the methods via the macro
extend-type which gets rid of the anonymous functions.
In case you want to define the methods on a per protocol basis, one can use
extend-protocol . It's also possible to extend types with a protocol right when they get declared.
One might be wondering why you would want to use
defrecord in combination with
defprotocol when there are multimethods . Multimethods are simpler in the sense that they are just functions with a dispatch method. They let you dispatch on any computation of the arguments. This also makes them slower but also more powerful. One can think of multimethods as superset of protocols.
With protocols you hook into the low level support of the JVM which is a lot faster. The downside is one can only dispatch on the type of the first argument. An advantage of protocols is that you can group methods based on a protocol (see the
Graph protocol above).
extends? also let one check if a value satisfies a protocol.
Although you can achieve a similar result with
isa? for multimethods, it needs to be done explicitly.
Two constructs this post hasn't touched upon are
reify serves essentially to define one-off types.