Search code examples
clojuremultimethod

Clojure defining multimethods and how to pass parameters


I seem to have a hard time understanding how the below code works. And more precisely how the defined function handles passed parameters

(defmulti encounter
    (fn [x y] [(:role x) (:role y)]))

(defmethod encounter [:manager :boss] [x y]
    :promise-unrealistic-deadlines)

(defmethod encounter [:manager :developer] [x y]
    :demand-overtime)
....

Why do we have 2 vectors ([x y] [(:role x) (:role y)]) when defining the "encounter". Does this mean that the function takes to vector parameter? If so why do I have to call the function like:

(encounter {:role :manager} {:role :boss})

Isn't the above call passing the first hashmap to [x y] and the second to [(:role x) (:role y)]). I just can't understand how does x get the value :manager and y gets the value of :boss.

The above example is from here: https://yogthos.github.io/ClojureDistilled.html


Solution

  • The second argument passed to the defmulti macro is called a dispatch function. Here, it accepts two arguments, x and y, each of which is expected to be a map with a :role key in it. The value returned by the dispatch function is called a dispatch value. It is being compared against whenever you call encounter.

    Every definition of the encounter method takes some dispatch value as its second argument. In your example, that value is produced by passing the arguments x and y into the dispatch function (fn [x y] [(:role x) (:role y)]) inside defmulti. Depending on the value returned by that function, either a corresponding method is invoked, or an IllegalArgumentException is thrown:

    (encounter {:role :designer} {:role :developer})
    

    produces

    IllegalArgumentException No method in multimethod 'encounter' for dispatch value: [:designer :developer]  clojure.lang.MultiFn.getFn (MultiFn.java:156)
    

    But adding a new possible dispatch value fixes it:

    (defmethod encounter [:designer :developer] [x y]
      :discuss-video-games)
    
    (encounter {:role :designer} {:role :developer})
    => :discuss-video-games
    

    There's also a dedicated clojuredocs page with more good examples.