Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is meant by 'Clojure supports multiple taxonomies' in relation to polymorphism?

Tags:

clojure

I have been reading the 'Clojure Rationale' here:

http://clojure.org/rationale

In the Polymorphism section it reads:

Clojure multimethods decouple polymorphism from OO and types

   Supports multiple taxonomies
   Dispatches via static, dynamic or external properties, metadata, etc

What is meant by 'supports multiple taxonomies' here? I fact, what is a taxonomy in this case? Thanks

like image 951
Zuriar Avatar asked Sep 12 '14 20:09

Zuriar


2 Answers

user> (defmulti shade :color)
nil
user> (defmethod shade :black [_] "darkest of darks")
#<MultiFn clojure.lang.MultiFn@22b90f93>
user> (defmethod shade :white [_] "all frequencies are reflected")
#<MultiFn clojure.lang.MultiFn@22b90f93>
user> (defmulti movement :legs)
#'user/movement
user> (defmethod movement 2 [_] "walks on hind legs")
#<MultiFn clojure.lang.MultiFn@13b58075>
user> (defmethod movement 4 [_] "proceeds on all fours")
#<MultiFn clojure.lang.MultiFn@13b58075>
user> ((juxt movement shade) {:name "cat" :legs 4 :color :black})
["proceeds on all fours" "darkest of darks"]

In the above code, two systems of organization are created - one in terms of color, the other in terms of legs. Both of these taxonomies are equally valid, and the same object can fall into different places depending on the taxonomy used.

Multimethods can also use hierarchies for dispatch (see derive and related functions), where each hierarchy can co-exist in parallel (unlike the unified view of Class hierarchies).

user> (derive ::manx ::cat)
nil
user> (defmulti favorite-treat :species)
#'user/favorite-treat
user> (defmethod favorite-treat ::cat [_] "Tuna")
#<MultiFn clojure.lang.MultiFn@264d27e6>
user> (derive ::indoor ::domestic)
nil
user> (defmulti activity :tameness)
#'user/activity
user> (defmethod activity ::domestic [_] "window watching")
#<MultiFn clojure.lang.MultiFn@1654bf3f>
user> ((juxt favorite-treat activity) {:species ::manx :tameness ::indoor})
["Tuna" "window watching"]

here the same kitty is a member of two hierarchies - one of domestication, and the other of genetics, and is can have its methods resolved by either as appropriate.

Also, even setting asid multimethods, relationships created via derive support multiple inheritance, unlike the jvm Class heirarchy Clojure is built on:

user> (derive ::foo ::bar)
nil
user> (derive ::foo ::baz)
nil
user> (derive ::quux ::foo)
nil
user> (isa? ::quux ::foo)
true
user> (isa? ::quux ::bar)
true
user> (isa? ::quux ::baz)
true
user> (isa? ::bar ::baz)
false
like image 189
noisesmith Avatar answered Nov 07 '22 01:11

noisesmith


Clojure, like many other functional languages, takes advantage of loose typing to provide easy ways to implement parametric polymorphism. This basically means that a single method can be constructed in a way that it does not care about the types of values the arguments it is given are.

Take for example the concat method. It takes any number of arguments of varying forms and puts them into a single list like so:

user=> (concat [:a :b] nil [1 [2 3] 4])
(:a :b 1 [2 3] 4)

Because one does not need to declare a typing for arguments, concat can be written in such a way that you can provide an argument of any type (vector, function, keyword, string, etc.) and it will act upon them in a similar way.

Clojure's multimethods allow you to support this concept on Java objects that may have completely different structures (i.e. taxonomies) by using metadata or other properties to determine the appropriate way to deal with the given argument. See the following example (taken from defmulti):

(defmulti greeting
  (fn[x] (x "language")))

(defmethod greeting "English" [params]
  (str "Hello! " (params "id")))
(defmethod greeting "French" [params]
  (str "Bonjour! " (params "id")))

=>(greeting {"id" "1", "language" "English"})
"Hello! 1"
=>(greeting {"id" "2", "language" "French"})
"Bounjour! 2"

The greeting method returns the "language" value from the map which matches the "English" or "French" defmethod which returns the correct corresponding value. Hopefully, you can see how this concept could potentially be applied to just about any kind of data or object structure. This powerful idea of polymorphism is what the Clojure developers are trying to show off in the rationale page.

like image 44
Chase Sandmann Avatar answered Nov 07 '22 03:11

Chase Sandmann