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.