A Clojure protocol names a set of method signatures for polymorphic, type-based dispatch. It creates a corresponding Java interface and exposes ordinary Clojure functions for invoking the methods. section 15.12 section 15.8
Define a protocol when conditional type checks are becoming difficult to maintain:
- Use defprotocol with a name and method signatures such as (method1 [this]). Each declared method becomes a dispatch function. section 15.8
- Implement the protocol directly with deftype, defrecord, proxy, or reify, or extend an existing class with extend, extend-type, or extend-protocol. section 15.8 section 15.9
- Dispatch normally uses the first argument's type. With metadata extension enabled, lookup checks the class, metadata, and then the extension table. section 15.8
- Prefer protocols over cascading instance? tests when efficient type dispatch is the goal; use multimethods when dispatch needs greater flexibility. section 15.2 section 15.12
Extend a protocol to several types
(defprotocol Money (as-currency [n])) (extend-protocol Money Integer (as-currency [n] (format "%s$" n)) clojure.lang.Ratio (as-currency [n] (format "%s$ and %sc" (numerator n) (denominator n)))) (as-currency 3) ; "3$" (extenders Money) ; (java.lang.Integer clojure.lang.Ratio) (extends? Money Integer) ; true Direct implementation is fixed on the generated class; extension can be added or changed later for all instances of a type. section 15.9
(defprotocol INode (value [_])) ; #1
(defprotocol IBranch (left [_]) (right [_]))
(defprotocol ILeaf (compute [_]))
(extend user.INode IBranch) ; #2
(extend user.INode ILeaf)
(defrecord Branch [id left right] ; #3
INode (value [_] (str "Branch::" id))
IBranch (left [_] left) (right [_] right))
(defrecord Leaf [id]
INode (value [_] (str "Leaf::" id))
ILeaf (compute [_] (str "computed:" id)))
(def tree ; #4
(->Branch 1
(->Branch :A (->Leaf 4) (->Leaf 5))
(->Branch :B (->Leaf 6) (->Leaf 7))))
(defn traverse
([tree]
(traverse [] tree))
([acc tree]
(let [acc (conj acc (value tree))] ; #5
(if (satisfies? IBranch tree) ; #6
(into
(traverse acc (left tree))
(traverse acc (right tree)))
(conj acc (compute tree))))))
(traverse tree) ; #7
;; ["Branch::1" "Branch:::A" "Leaf::4" "computed:4"
;; "Branch::1" "Branch:::A" "Leaf::5" "computed:5"
;; "Branch::1" "Branch:::B" "Leaf::6" "computed:6"
;; "Branch::1" "Branch:::B" "Leaf::7" "computed:7"]
A protocol-based tree example connects node, branch, and leaf interfaces, then uses satisfies? during recursive traversal. section 15.9

