~ / track B / clojure advanced
Protocols and multimethods
AdvancedpracticeClojure offers two different "polymorphism" tools. Protocols dispatch on the type of the first argument and compile down to fast JVM method calls. Multimethods dispatch on the result of an arbitrary function — type, a keyword, a tuple, anything you can compute. Protocols are for the common "behave-like-this-thing" case; multimethods are for everything else. Both are practical solutions to the expression problem — extend operations and variants without editing existing code — especially when domain entities are plain maps from modeling with values.
Minimal example: a protocol
A protocol declares a small interface, and any number of types can extendclojure.core/extendProgrammatic, lower-level form for extending a type to a protocol.view on clojuredocs → it.
Protocols are open: a new file can come along and extend-protocolclojure.core/extend-protocolExtend a protocol to one or more types.view on clojuredocs → to types the protocol's author never imagined, without modifying the original code.
Records implement protocols natively
defrecordclojure.core/defrecordDefine a record: a typed, map-like class implementing protocols.view on clojuredocs → creates a Java class that can implement protocols inline, which is the typical "object-like" pattern in Clojure:
Multimethods dispatch on anything
Where a protocol asks "what type is this?", a multimethod asks "what should I look up in the method table?". The dispatch function computes the key.
The dispatch function can return a tuple, enabling a kind of double dispatch:
When to use which
- Protocol: you have a small, fixed set of operations and you want them fast; dispatch is naturally per-type; you might also use a record.
- Multimethod: dispatch depends on data (a keyword field, a tuple, a
hierarchy via
derive); flexibility matters more than raw speed.
Check yourself
? quiz
A new payment method ships and you want to extend an existing protocol to it. Do you need to modify the protocol's source file?
Exercise
Define a multimethod tax-of that dispatches on :country and computes a tax
amount from :price. Implement :br (10%), :us (7%), and a :default of 0.