- The document discusses different approaches for implementing dynamic polymorphism in Clojure: late namespace binding, protocols, and multimethods.
- Late namespace binding allows dynamically switching namespaces at runtime but has issues with testing. Protocols define interfaces but require defining a protocol for each participant.
- Multimethods provide true runtime polymorphism without macros or configuration by using defmulti and defmethod to dispatch on runtime values and define implementations. They require less code and allow easy test injection.
1. Dynamic Polymorphism
in Clojure
-or-
How I learned to stop
configuring and start loving
multimethods
Scott Shaw <scottwshaw@gmail.com>
Friday, 11 May 12
2. Outline
• Intro - a bit about me
• The problem
• Approaches
• Late namespace binding
• Protocols
• Multimethods
Friday, 11 May 12
3. About Me
• Reformed academic
• wrote 1000’s of lines of bad Lisp code
back in the day
• Now, IT consultant
• developer, architect, manager,
technologist, consultant for
ThoughtWorks in AsiaPac
Friday, 11 May 12
10. the macro
(defmacro with-namespace-binding
"Takes a single [namespace-keyword function] pair"
[[namespace-id function] & body]
(let [the-namespace (namespace-id (namespace-bindings))]
`(do
(when-not (find-ns '~the-namespace) (require '~the-namespace))
(let [~function (ns-resolve '~the-namespace '~function)]
~@body))))
Friday, 11 May 12
11. Summary
• Not very intrusive
• Just wrap the function call in a
(let [:namespace function] ...) form
• Don’t have to anticipate the potential
namespaces in the calling function (no explicit
require)
• Testing isn’t great
• tests are run after macro expansion so
difficult to inject behaviour without
configuration.
Friday, 11 May 12
12. Maybe we should be
using protocols instead?
Friday, 11 May 12
13. Protocols
• Abstraction and contracts without the OO
concept of inheritance
• Define protocols for each participant,
Model and DataSource.
• In a configuration file, define a wiring
pattern of datatypes for each
environmental option
• You end up with a typical OO dependency
injection framework (with an implicit
constructor function)
Friday, 11 May 12
18. Summary
• Heading toward a typical OO interface-
driven dependency-injection framework
• Am I rewriting Spring in Clojure?
• Fairly intrusive in that it requires a protocol
to be defined for each participant
• Nice ability to inject behaviour in tests
• Still hard to test the framework itself
Friday, 11 May 12
19. OK, I suppose I should
try out multimethods
since it is the “official”
Clojure approach to
runtime polymorphism
Friday, 11 May 12
20. multimethods
• Use defmulti to declare a method name
and a function to be used for dispatch
• Use defmethod to define functions for each
possible dispatch value
• In our case...
• define a generic set of multimethods that
a data-source must implement
• create new data sources by defmethods
• the target environment is encoded in the
method implementation
Friday, 11 May 12
23. Summary
• Least code of all the solutions by far
• True runtime polymorphism
• No ugly macros
• Easy to inject behaviour for tests
• No configuration!
• Model must be aware of implementations
to a certain extent
• (require ‘[all possible namespaces that
might be dispatched])
Friday, 11 May 12