Multimethods in Clojure
I am continuously amazed by tic-tac-toe. Implementing the game is definitely a good practice in learning a new language. While it requires rudimentary knowledge of the language, there are definitely more advanced topics you can incorporate into your game (e.g. implementing protocols, using atoms, and having an AI that uses the minimax algorithm).
The latest and greatest lesson I’ve learned is about multimethods, which in addition to protocols, is another way to handle polymorphism in Clojure. Before implementing multimethods in my code, I had about 100 lines of code performing the same action with different arguments. They were essentially prompting the user for input, validating that input, and either returning the value of the input (if valid) or re-prompting the user (if invalid).
Something felt funny. I knew that I could abstract a method or two and just call those methods several times, as needed.
Once that was accomplished, I had a series of multi-arity functions that were very similar, except that they passed in different arguments.
All of these functions are linked and related — if I refactored it, the distinction could be in their types. I realized I could do something about this!
A multimethod is defined as “a combination of a dispatch function and one or more methods, each defining its own dispatch value. The dispatching function is called first, and returns a dispatch value.”
What’s great about this is that if we added another option (for example, if we wanted to ask for the user’s name or their preferred marker piece, besides an “X” or “O”), it would be very simple to add! Literally, it would take three lines of code, plus a couple of lines of code for string values (e.g. a message prompt like “Enter your name:”) and some kind of validation (e.g your marker piece must be a single character). Talk about a flexible and extensible architecture!
Here is what my code looks like. A multimethod is defined by using `defmulti` (line 1). The other methods create and install a new method of multimethod associated with a dispatch-value, as defined by `defmethod` (lines 3, 6, 9, and 12). When defined, they will be called with an argument (in this case, `option`) and therefore serve as the value of the multimethod call. If there isn’t a method associated with the dispatching value, the multimethod will seek a method that is used as the default (`:default`). Since I don’t anticipating this happening (I call each function once at the start of the game), I didn’t set one up.
Two of my biggest issues was trying to figure out how to:
- Self-reference the method in the method
- Implement arity-overloading in the multimethod.
There were a few Stack Overflow posts I read and re-read to no avail. I’m still trying to figure these out and if I accomplish this, will write about them. In the interim, what I’ve decided to do was just have the methods call other functions. This works just fine, but would be great to leverage multi-arity, which `defmethod` and `defmulti` both seem to support.
There is a lot more to multimethods, including the ability to define a hierarchy. previously mentioned blog post talks about using `derive` and `isa?`to match dispatch values to the correct method. Very cool! While it was not necessary for my use (since the dispatch value is really calling another function), it’s useful for other implementation of multimethods.