Polymorphism in Clojure
Reading time: 3 minsFirst lets define what is Polymorphism. Simply put, polymorphism is about having a single interface for different data types.
Different types of polymorthism
There are different types, some with different names.
- Static binding Time
This is related to method overloading - Dynamic binding
Related to method overriding
[code lang="java"]
interface Animal {
void makeNoise ();
}
Dog implements Animal {
public void makeNoise() {
System.out.println("Woof!");
}
}
Cat implements Animal {
public void makeNoise() {
System.out.println("Meow!");
}
}
class Main {
public static void main(String args[]) {
Animal cat = new Cat();
Animal dog = new Dog();
cat.makeNoise();//output: "Meow!"
dog.makeNoise();//output:"Woof!"
}
}
[/code]
The output changes because of the different Animal data type.
Clojure being a dynamic language, we will be focusing on dyanmic polymorphism
Problem
You want to build function that changes dependent on the argument given.
Possible Solutions
An already existing used of Polymorphism in Clojure is the cons method which will add an element to a given list/vector/set input and return the result.
We could use if-branching, cond or condp. But these aren't really polymorphic.
Lets have a look below.
[code lang="clojure"]
(defn greet [name]
(if (empty? name)
(str "hello annon, how are you?")
(str "hello " name ", how are you?")))
[/code]
This could work but it's not idiomatic. Clojure has a better way of handling this kind of scenario.
We have a few possible solutions in Clojure that we could use to perform our own polymorphism.
Multi-arity functions
Arity is simply the number of input arguments in the function.
[code lang="clojure"]
(defn greet
([] (greet "anon"))
([name] (str "hello " name ", how are you?"))
([name & more] (str "hello "
(apply str
(interpose ", "
(cons name more)))
", how are we all?")))
[/code]
The greet function overloads itself when no argument is passed in, but what we can also do is handle more than 2 arguments are passed in.
Multi-methods
Multi-methods is a great way to introduce polymorphism. It reminds me when you go to the theatre. The usher takes your ticket(the input) and then directs you where you should sit(output) depending on whether you're in premium seating, front row etc.
Clojure achieves this behaviour by defining a dispatching function with dispatching values which then determine the appropriate method to call.
[code lang="clojure"]
(defmulti dance-style(fn [input] (:style input)))
(defmethod dance-style (:hip-hop [a] (str (:name a) " loves to pop and lock."))
(defmethod dance-style :ballet [a] (str (:name a) " loves to gracely move."))
(defmethod dance-style :default [a] ( str "I don't know what " (:name a) ". Has he's own style"))
(dance-style {:style :hip-hop :name "Cedric" :age 20 :experience :mid-level})
(dance-style {:style :ballet :name "Jada" :age 4 :experience :junior})
(dance-style{:name "Chuck Norris"})
[/code]
So now when we call
[code lang="clojure"]
(defn get-dance-style [style]
(dance-style style))
=>"Cedric loves to pop and lock."
[/code]
Another thing you can do is have multi-methods across namesapces. This can be a good way to separate the different dance styles(in this example)
So you can then place all the hip-hop related dancers in the same namespace, all the ballet in their own namespace.