Moving Beyond Simple Dispatch

Clojure’s print function prints various “sequencey” things as lists. If you wanted my-print to do something similar, you could add a method that dispatched on a collection interface high in the Java inheritance hierarchy, such as Collection:

 (require '[clojure.string :as str])
 (​defmethod​ my-print java.util.Collection [c]
  (.write *out* ​"("​)
  (.write *out* (str/join ​" "​ c))
  (.write *out* ​")"​))

Now, try various sequences to see that they get a nice print representation:

 (my-println (take 6 (cycle [1 2 3])))
 | (1 2 3 1 2 3)
 -> nil
 
 (my-println [1 2 3])
 | (1 2 3)
 -> nil

Perfectionist that you are, you cannot stand that vectors print with rounded braces, unlike their literal square-brace syntax. So add yet another my-print method, this time to handle vectors. Vectors all implement an IPersistentVector, so this should work:

 (​defmethod​ my-print clojure.lang.IPersistentVector [c]
  (.write *out* ​"["​)
  (.write *out* (str/join ​" "​ c))
  (.write *out* ​"]"​))

But it doesn’t work. Instead, printing vectors now throws an exception:

 (my-println [1 2 3])
 -> java.lang.IllegalArgumentException​:​ Multiple methods match
 dispatch value​:​ class clojure.lang.LazilyPersistentVector ->
 interface clojure.lang.IPersistentVector and
 interface java.util.Collection,
 and neither is preferred

The problem is that two dispatch values now match for vectors: Collection and IPersistentVector. Many languages constrain method dispatch to make sure these conflicts never happen, such as by forbidding multiple inheritance. Clojure takes a different approach. You can create conflicts, and you can resolve them with prefer-method:

 (prefer-method multi-name loved-dispatch dissed-dispatch)

When you call prefer-method for a multimethod, you tell it to prefer the loved-dispatch value over the dissed-dispatch value whenever there’s a conflict. Since you want the vector version of my-print to trump the collection version, tell the multimethod what you want:

 (prefer-method
  my-print clojure.lang.IPersistentVector java.util.Collection)

Now, you should be able to route both vectors and other sequences to the correct method implementation:

 (my-println (take 6 (cycle [1 2 3])))
 | (1 2 3 1 2 3)
 -> nil
 
 (my-println [1 2 3])
 | [1 2 3]
 -> nil

Many languages create complex rules, or arbitrary limitations, to resolve ambiguities in their systems for dispatching functions. Clojure allows a much simpler approach: just don’t worry about it! If there’s an ambiguity, use prefer-method to resolve it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.224.226