~ / track B / clojure advanced

Protocols and multimethods

Advanced

Clojure 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.

Minimal example: a protocol

A protocol declares a small interface, and any number of types can extend it.

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

Protocols are open: a new file can come along and extend-protocol to types the protocol's author never imagined, without modifying the original code.

Records implement protocols natively

defrecord creates a Java class that can implement protocols inline, which is the typical "object-like" pattern in Clojure:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

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.

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

The dispatch function can return a tuple, enabling a kind of double dispatch:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

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.

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate
 status: new