Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.

Multimethods 97 Languages such as Dylan, Nice, R, and Common Lisp support multiple dispatch. Clojure also supports multiple dispatch through its multimethods feature. 4.3 Multimethods Now that you've seen how method polymorphism works underneath, we're finally ready to look at Clojure's multimethod feature. Clojure multimethods support not only multiple dispatch but much more. Indeed, once you look past multiple dispatch, a commonly asked question is whether a language can dispatch on things other than the types of values. With Clojure's multimethods, methods can be made to dispatch based on any arbitrary rule. A multimethod is defined using defmulti . A multimethod by itself isn't useful; it needs candidate implementations from which to choose when it's called. The defmethod macro is used to define implementations. Let's take a look. 4.3.1 Without multimethods Consider the situation where our expense-tracking service has become popular. We've started an affiliate program where we pay referrers if they get users to sign up for our service. Different affiliates have different fees. Let's begin with the case where we have two main ones: mint.com and the universal google.com. We'd like to create a function that calculates the fee we pay to the affiliate. For the sake of illustration, let's decide we'll pay our affiliates a percentage of the annual sal- ary the user makes. We'll pay Google 0.01%, we'll pay Mint 0.03%, and everyone else gets 0.02%. Let's write this without multimethods first: (defn fee-amount [percentage user] (float (* 0.01 percentage (:salary user)))) (defn affiliate-fee-cond [user] (cond (= :google.com (:referrer user)) (fee-amount 0.01 user) (= :mint.com (:referrer user)) (fee-amount 0.03 user) :default (fee-amount 0.02 user))) The trouble with this way of writing this function is that it's painful to add new rules about affiliates and percentages. The cond form will soon get messy. We'll now rewrite it using Clojure's multimethods. 4.3.2 Using multimethods Before we implement the same functionality using multimethods, let's take a moment to understand how they work. As mentioned earlier, multimethods are declared using the defmulti macro. Here's a simplified general form of this macro: (defmulti name dispatch-fn & options) The dispatch-fn function is a regular Clojure function that accepts the same arguments that are passed in when the multimethod is called. The return value of