Clojure Protocols and Records Guide — Polymorphism and Data Types
In this tutorial, you will learn about Clojure Protocols and Records Guide. We cover key concepts, practical examples, and best practices to help you master this topic.
Clojure protocols define sets of polymorphic functions using defprotocol, records (defrecord) implement protocols with map-like behavior (assoc/dissoc/get), and types (deftype) provide bare-metal Java interop with no map semantics for maximum performance.
Defining Protocols
;; Define a protocol
(defprotocol Shape
"Geometric shape protocol"
(area [this] "Calculate area")
(perimeter [this] "Calculate perimeter"))
;; Implement protocol
(defrecord Circle [radius]
Shape
(area [this] (* Math/PI radius radius))
(perimeter [this] (* 2 Math/PI radius)))
(defrecord Rectangle [width height]
Shape
(area [this] (* width height))
(perimeter [this] (* 2 (+ width height))))
Using Records
;; Create record instances
(def c (->Circle 5.0))
(def r (->Rectangle 3.0 4.0))
;; Protocol dispatch
(area c) ;; 78.539...
(perimeter r) ;; 14.0
;; Map-like behavior (records are associative)
(:radius c) ;; 5.0
(get c :radius) ;; 5.0
(assoc r :width 5.0) ;; new rectangle with updated width
;; Factory functions
(Circle. 5.0)
(map->Circle {:radius 5.0})
Extending Protocols
;; Extend existing types to protocols
(defprotocol Show
(show [this]))
;; Extend Java String
(extend-protocol Show
String
(show [s] (str "\"" s "\""))
Number
(show [n] (str "Number: " n))
Object
(show [o] (str "Object: " (class o))))
(show "hello") ;; "\"hello\""
(show 42) ;; "Number: 42"
deftype
;; Low-level type (no map semantics)
(deftype Point [x y]
Object
(toString [this] (str "Point(" x "," y ")")))
;; Access fields directly
(let [p (Point. 3 4)]
(.x p)) ;; 3 (direct field access)
;; Can implement protocols
(deftype MutableCounter [^:unsynchronized-mutable count]
clojure.lang.IFn
(invoke [this] (set! count (inc count)) count))
Reify
;; Anonymous protocol implementation
(defn create-shape [radius]
(reify Shape
(area [this] (* Math/PI radius radius))
(perimeter [this] (* 2 Math/PI radius))))
(let [s (create-shape 5)]
(area s)) ;; 78.539...
Common Mistakes
1. Records vs maps confusion
Records have fixed fields. (assoc r :non-existent val) creates a plain map, not a record. Check with (record? r).
2. Protocol method name conflicts
Protocol methods are namespaced. Two protocols can have the same method name if they're in different namespaces.
3. Not using reify for single-use implementations
For one-off protocol implementations, use reify instead of defining a named record or type.
Practice Questions
1. How do you define a protocol?
(defprotocol Name "doc" (method [this args] "doc") ...).
2. How do you create a record implementing a protocol?
(defrecord Name [fields] ProtocolName (method [this] body) ...).
3. What is the difference between defrecord and deftype? Records are associative (work with assoc/dissoc/get). deftype has no map semantics but allows mutable fields and IFn.
FAQ
{{< faq question="Can protocols extend Java classes?" >}}
Yes. Use (extend-protocol ProtocolName JavaClass (method [this] ...)) for classes.
{{< /faq >}}
{{< faq question="How does protocol dispatch work?" >}} Protocols use first-argument dispatch (like single dispatch). The implementation is chosen based on the type of the first argument. {{< /faq >}}
{{< faq question="Can I define default protocol implementations?" >}}
Yes. Use extend-type or extend-protocol with Object to provide a default implementation.
{{< /faq >}}
What's Next
Now learn about core functions in Clojure.
| Topic | Description | Link |
|---|---|---|
| Core Functions | Essential functions | {{< ref "11-core-functions" >}} |
| Error Handling | Exception Handling | {{< ref "12-error-handling" >}} |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro