Polymorphism Through Protocols

Image credit: The Internet

Take a look at the following code, which prompts the current player for a move. If the current player is the computer, it calls `make-computer-move`. If not, it will print the board and call `make-human-move`.

Let’s consider how the following code can be improved:

(defn prompt-for-move
[board player]
(if (is-computer? player)
(make-computer-move board player (switch-player player))
(do (print-board board)
(make-human-move board))))

If you’re thinking along the lines of calling a method based on type, rather than having an expression that checks for type — congrats, you are correct. We want to dispatch methods according to type and protocols are ideal for this situation. Protocols provide a uniform interface and abstract implementation by type. Think of a method would be able to handle different data types, regardless of what it is. This is, by very definition, polymorphism.

Why is it important? If we want to software extensible, we should have it accommodate various and new data types.

In Ruby, we enabled polymorphism through inheritance. In Clojure, we have protocols since inheritance is not built into the language. A protocol is defined using `defprotocol` which is a set of named methods and their signatures. In terms of namespacing, the protocol’s methods belong to the namespace that they’re defined in. In the example below, the fully qualified name of the single method in `Player` is `player/make-move`.

(ns tic-tac-toe.player)
(defprotocol Player
(make-move [this board]))

There are several ways to implement a protocol and even more ways to define new class-like datatypes (there are, in fact, five ways). Quite frankly, it was not very straightforward which was the best route. Several examples just showed one route (e.g. using `deftype`) and Clojure for the Brave and True only introduced `defrecord` (in addition to protocols and multimethods), deeming everything else as advanced topics. What I have found that `deftype` mimics an object and has mutable fields, whereas `defrecord` creates an immutable persistent map.

I found this super awesome flowchart from Chas Emerick that helped me make the decision to use `deftype` for implementing `Player`. See below:

Protocols are ideal for encapsulating related methods and an object-oriented solution for writing and organizing your code. Looking forward to implementing it in my game!