liveBook · Manning

Protocol

As explained in Clojure: The Essential Reference

A Clojure protocol is a named set of method signatures that types can implement and that is represented by a corresponding Java interface.

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

How Clojure: The Essential Reference covers it

How deep it goes
Explains generated interfaces, dispatch lookup, implementation strategies, extension timing, metadata, and basic performance differences.
Examples
Demonstrates protocol inspection, String extension, direct and later implementations, numeric formatting, and polymorphic tree traversal.
What it assumes
Assumes familiarity with Clojure types and functions while explaining protocol declarations, interfaces, dispatch, and extension from fundamentals.
What you can do after
Build it from scratch with defprotocol, implement methods directly, extend existing types, and inspect supported extensions.
How it connects
Connects protocols with type dispatch, multimethods, Java interfaces, records, types, metadata, and polymorphism.

Also explained in

Compare how 2 books explain protocol →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Clojure: The Essential Reference. The section links point to the parts of the book it draws on.