Object-Oriented Dispatch is the Dual of Functional Dispatch
Summary: Object-oriented dispatch is contrasted with functional dispatch, but they are shown to be two one-dimensional views of the same two-dimensional data. Clojure does not provide the two-dimensional representation, but does interesting things to transcend the one-dimensional views.
About a month ago, I wrote a post about how OO-style is the dual of functional-style. OO focuses on the data first, while functional focuses on the code. It is cool that the correspondence between the two is fairly clear and mechanical. It means that they're equivalent in a way. The distinction is mostly important when choosing how to represent a problem. A language should provide ways to express both (as Clojure does), and a way to translate between the two (which Clojure does not).
But it goes further than data vs code. In OO, the object is the unit of data (which manifests as the principle of encapsulation). But the object is also the unit of dispatch. Objects know their class, and classes know the implementations of their methods. Class first, behavior second. In a functional style of programming, the function is the unit of dispatch. A function knows how to react to all possible classes of arguments. Behavior first, class second.
Example:
(defn foo [x]
(cond
(string? x)
...
(integer? x)
...
(vector? x)
...
So again, we have a kind of duality. The classic way to explain this is to show it in a table.
method/class String
Integer
Vector
...
toString
. . .
length
. x .
+
x . x
*
x . x
...
A dot represents an implementation, whereas an x represents an undefined operation.
In OO style, the class represents a single column. In functional style, the function represents a single row. Logically, however, the information is a two-dimensional table. That brings up a question: why don't languages store the information in this form internally? It is easy to project a column view or a row view from a table. And the x's in the table seem to be really useful for static checks.
Clojure does not represent its functions this way. But it does allow you
to express your code in either a column view or a row view. The
row view is the standard functional approach shown above. The column
view is using deftype
.
Example:
(deftype Person
Object ;; indicating I'm overriding methods from Object
(toString [p]
...)
java.util.Comparable
(compareTo [p]
...)
...)
Clojure goes a step further: instead of having to write out the entire column (all methods given a class) in one place, you can essentially index directly into the table given a method/class pair, and define the implementation directly. This only works with protocol methods (not methods on classes/interfaces due to limitations in the JVM).
Example:
(extend-type Object
MyProtocol
(mymethod [x o]
...))
OO-style and functional-style are duals in terms of dispatch. It's very related to the data-first or code-first duality I wrote about before. Clojure again straddles both sides of the duality and lets you write code in both styles, as well as transcend the column/row distinction entirely when using protocols.
How does your language deal with dispatch? Can you express the problem in the best way? If you're interested in this kind of topic, you would probably enjoy the Clojure Gazette. It's a weekly newsletter filled with content to inspire Clojure programmers. It's completely free and it's easy to unsubscribe.
Thanks to Marcus Blankenship for the inspiration for this article.