(def my-square {:shape "square"})
(defmulti draw :shape)
(defmethod draw "square" [x]
"Rendering a square...")
(draw my-square)
;=> "Rendering a square..."
You need a lot of different types of people to make the world better.
(def my-square {:shape "square"})
(defmulti draw :shape)
(defmethod draw "square" [x]
"Rendering a square...")
(draw my-square)
;=> "Rendering a square..."
Keywords are functions
Common to use a keyword as a dispatch function
Looks similar to Object Oriented type dispatch
Users can add methods later
Polymorphic dispatch. Define the name and the dispatch function:
(defmulti encounter
(fn dispatch [a b]
[(:species a) (:species b)]))
;=> #'encounter
Dispatch is not limited to a single type
Dispatch might not even involve a type
(defmethod encounter [:bunny :lion] [a b] :run-away)
(defmethod encounter [:lion :bunny] [a b] :eat)
(defmethod encounter [:lion :lion] [a b] :fight)
(defmethod encounter [:bunny :bunny] [a b] :mate)
;=> #object[cljs.core.MultiFn]
Similar to a case block
Not limited to a single input
Input is unused in this example
(def bunny1 {:species :bunny, :other :stuff})
(def bunny2 {:species :bunny, :other :stuff})
(def lion1 {:species :lion, :other :stuff})
(def lion2 {:species :lion, :other :stuff})
(encounter bunny1 bunny2)
;=> :mate
(encounter bunny1 lion1)
;=> :run-away
(encounter lion1 bunny1)
;=> :eat
(encounter lion1 lion2)
;=> :fight
Conditions under which to be called + function definitions
Often dispatch by type, but not limited to that
Provide a point of extension
Clojure test reporter can be modified
JDBC types can have custom handlers added
Protocols directly implement host polymorphism (JVM)
Dispatch on the type of their first argument
Fast
User or library can add methods later
(defprotocol AProtocol "A doc string for AProtocol abstraction" (bar [a b] "bar docs") (baz [a] [a b] [a b c] "baz docs"))
A named set of named methods and their signatures
No implementations are provided
Dynamic
Generates a corresponding interface with the same name
The protocol will automatically work with instances of the interface
A Java client can implement the protocol-generated interface
(defprotocol P (foo [x]) (bar [x] [x y]))
(deftype T [a b c] P (foo [x] a) (bar [x] b) (bar [x y] (+ c y)))
(bar (T. 1 2 3) 42) => 45
(def obj (reify P (foo [this] 17))) (foo obj) => 17
Creates an object that implements a protocol without defining a type
Do not have to implement all protocol signatures
Can also reify Java interfaces
Java classes are closed
Java interfaces cannot be extended
(extend AType AProtocol {:foo an-existing-fn :bar (fn [a b] ...) :baz (fn ([a]...) ([a b] ...)...)} BProtocol {...} ...)
The fn
can presume first argument is instanceof AType
You can implement a protocol on nil
Default implementation of protocol with Object
(extend-type MyType Countable (cnt [c] ...) Foo (bar [x y] ...) (baz ([x] ...) ([x y zs] ...)))
Expands into:
(extend MyType Countable {:cnt (fn [c] ...)} Foo {:baz (fn ([x] ...) ([x y zs] ...)) :bar (fn [x y] ...)})
User or library can add functionality later
Embrace the host (JVM or JavaScript)
Use when extension is required
Create when extension is anticipated
deftype
, defrecord
, and reify
define implementations of abstractions, and instances of those implementations.
Resist the urge to use them to define 'structured data' as you would define classes or structures in other languages.
It is preferred to use the built-in datatypes (vectors, maps, sets) to represent structured data.
(deftype Circle [radius]) (deftype Square [length width])
(Circle. 10) (Square. 5 11)
(->Circle 10) (->Square 5 11)
No protocol required
(ns training.core (:import (java.net FileNameMap)))
(defrecord Thing [a] FileNameMap (getContentTypeFor [this fileName] (str a "-" fileName)))
Defines a record named Thing
single field a
FileNameMap
interface
String getContentTypeFor(String fileName)
(def thing (Thing. "foo"))
(instance? FileNameMap thing) => true
Call the method on the thing
instance and pass "bar"
:
(.getContentTypeFor thing "bar") => "foo-bar"