Learning Clojure/Multimethods and polymorphism
Multimethods and polymorphismEdit
Most languages treat encapsulation and implementation inheritance as the primary features of object-oriented programming, but Clojure thinks these things are overrated. The real virtue of OOP, Clojure says, is polymorphism---encapsulation and inheritance are simply straight-jackets holding back polymorphism's potential.
When doing object-oriented programming in Clojure, we simply use structmaps in place of objects while, in place of traditional encapsulated methods, we use what Clojure calls multimethods, functions which pass on their arguments to other functions based on some criteria of the arguments, such as the number and/or type of the arguments.
A multimethod is made up of three things: a dispatch function, a set of methods, and a set of values associated with those methods. Calling a multimethod calls the dispatch function, and the value returned determines which method to call. A multimethod is created with clojure/defmulti and methods added to it by the macro clojure/defmethod. Here's a reductively simple multimethod with just two methods:
(defmulti victor (fn [a] a)) (defmethod victor 3 [a] "hello") ; attach a method to call when the dispatch returns 3; the function takes 1 argument, ; which it ignores, returning "hello" (defmethod victor 5 [a] "goodbye") (victor 3) ; returns "hello" (victor 5) ; returns "goodbye" (victor 4) ; exception: No method for dispatch value: 4
Note that the methods' arities must match the multimethod arity because the dispatch function calls the methods by passing on its arguments. (Also note the stylistic inconsistency: defmulti expects a function argument whereas defmethod imitates the defn form.)
Multimethods can be used to imitate traditional single- and multiple-inheritance polymorphism as well as more flexible kinds of polymorphism.