Saturday, November 19, 2011

Object orientation is more than a language primitive

Distinguishing object oriented languages from non-object oriented languages is like talking about free trade and regulated trade. All trade is regulated in both extreme and subtle ways; it's never free (think about slave trade, child labour, pollution control, safety, truth in advertising, and so forth). So called object orient languages provide primitives generally for just one object oriented abstraction; so for any given object oriented language there are many object oriented abstractions and features that it doesn't implement. All object oriented languages are just some person's or group's preferred abstraction (or merely easily implemented abstraction), not at all a universal truth.

I have a distrust of coupling my domain abstraction to language features. A lot of what a like about using functional style in imperative languages is that it helps me to recognise and explicitly represent some things that I might overlook if I just stayed in the mindset of the imperative language I'm working with. Object orientation abstraction is one thing regularly overlooked and left as a non choice. We generally fail to ask the question: what is the right way to use object orientation in this specific application?

It's important to me to recognise what different object oriented abstractions provide: different ways of expressing type relationships, different ways of sharing common code, different ways of protecting access to code and data, different ways to communicate between objects, different ways to get new objects, and so forth. With that understanding I want to build the most appropriate object oriented abstraction to get the benefits most relevant to my particular problem.

One tool to help reach this end is to sneer a little bit at the object oriented primitives in whatever language I'm using. By not letting myself lazily treat that set of primitives as automatically the right way to do object orientation I make myself ask valuable questions: what is the right way to partition code in this application, what is the right way of expressing type in application, what states or life cycle will my object pass through, and much more.

This has recently been brought home to me in discussions about programming Lisp. Programmers that I respect say they have no idea where to start writing a program in Lisp. I think they expect to have the language tell them to organise their code. But really in Lisp, beyond the beginners stage, you're job is to decide how to organise your code, how to represent object orientation if that's part of your application, or how to model data flow, or logical structure, how to represent what ever is important in your application.

In the JavaScript world there are a number of systems that offer support for building object hierarchies, providing abstractions that are generally more like Java's object oriented abstraction. This is a great thing if that object oriented model is the right abstraction for your client side programming. Sadly I think it's been done because people find it difficult to see object orientation as a choice of application specific abstraction rather than a constant of the tools they are using, and aim to make all their tools homogenous with regard to object oriented abstraction.

Oddly, the best object oriented code I've ever dealt with has been in C and Lisp, probably because the developers made choices about organising their code, and didn't treat the primitives of the language as the end of the story. Higher level abstractions of data flow, and of aggregating data and function, and object life cycle, were clearly chosen and expressed in code. Avoiding accidental design driven by language primitives.

Saying or acting as if there can be only one object oriented abstraction and one set of perfect primitives implementing it is frankly and obviously silly. There are many object orient abstractions out there being used productively every day. Good programmers should be able to choose the abstraction that's right for the application and implement it in the language that they are using for the application.

Tuesday, November 8, 2011

Dynamic dispatch is not enough to replace the Visitor Pattern

I've been bugged a little by how poorly understood the Visitor pattern is by people claiming it's not needed in their particular language because of some funky feature of the language. In the Clojure world dynamic dispatch or multi-methods are the supposed giant killer, (and I'm an Lisp fan of old, and Clojure is fast growing on me, this will be a rant about the fans who rail against an enemy and get it wrong).

The Visitor pattern has two parts, an Iterator and Dispatcher.

The mistake is that because Clojure has dynamic dispatch then it must be the dispatcher that gets replaced. In fact the dynamic dispatch really can help with the iterator, allowing different things in the structure to be treated differently. But as the example below shows it's not sufficient for some even more arbitrary dispatch types, not without adding extra information to parameters to facilitate the dispatch, which is an unnecessary added complexity; unnecessary added complexity being my basic definition of bad design.

My favourite example is a tree traversal engine, for something like an AST, with data like this: (def tree '(:a (:b :c :d) :e))

Now I want to do arbitrary things to this tree, for example, render it as JSON data:

{"fork": ":a",
 "left": {
   "fork": ":b",
   "left": ":c",
   "right": ":d"},
 "right": ":e"}

And also as an fully bracketed infix language: ((:c :b :d) :a :e)

There is absolutely nothing, but nothing, neither in the data nor the traversal that can be used to distinguish between rendering as JSON data or rendering for an infix language. This not a candidate for any sort of algorithmic dynamic dispatch based on any part of the data: the distinction is arbitrary based on functions chosen without regard to the data.

It's quite fair to note that there are two different types of nodes, branching and terminal, that need to be traversed in different ways, which can be usefully used for dynamic dispatch, but in that's dynamic dispatch in the traversal engine not the final functional/Visitor dispatch.

First define a generic traversal:

(declare ^:dynamic prefix 
         ^:dynamic infix 
         ^:dynamic postfix 
         ^:dynamic terminal)

(defmulti traverse #(if (and (seq? %) (= 3 (count %))) :branching :leaf))

(defmethod traverse :branching [[fork left right]] 
  (do
    (prefix fork)
    (traverse left)
    (infix fork)
    (traverse right)
    (postfix fork)))

(defmethod traverse :leaf [leaf]
  (terminal leaf))

Nice application of multi-methods and dynamic dispatch. Note that in the most recent versions of Clojure (1.3.0) dispatch defaults to a slightly faster common case of non-dynamic dispatch, so you have to declare the dynamic binding type. If you know you need dynamic dispatch you need to tinker with meta-data, and you can do that to other people's code from outside: (alter-meta! #'their-symbol assoc :dynamic true)

For ease sake I'm also going to provide a default set of behaviour, doing nothing. Notice the matching dynamic binding type specified as meta-data:

(defn ^:dynamic prefix   [fork] nil)
(defn ^:dynamic infix    [fork] nil)
(defn ^:dynamic postfix  [fork] nil)
(defn ^:dynamic terminal [leaf] nil)

Now to get it to do something real, rendering in a fully bracketed in infix style. Notice that binding doesn't need explicit dynamic meta-data, because dynamic is the heart of binding:

(binding [prefix   (fn [_] (print "("))
          infix    (fn [fork] (print " " fork " "))
          postfix  (fn [_] (print ")"))
          terminal (fn [leaf] (print leaf))]
  (traverse '(:a (:b :c :d) :e)))

How about printing the symbols space separated in reverse polish notation, as if for a stack language like PostScript or Forth. Notice how I only need to provide two of the four possible bindings thanks to the default handlers.

(binding [postfix  #(print % " ")
          terminal #(print % " ")]
  (traverse '(:a (:b :c :d) :e)))

The fully bracketed rendering illustrates one other very naive mistake with many peoples understanding of the Visitor pattern. Some nodes are the data to more than one function call during the traversal, the distinction being dependent on the traversal pattern and the nodes location in the structure. The final dispatch to the is not determined by the data of the node in isolation, the individual node data being passed to the function is insufficient to choose the function.

There are some useful applications of multi-methods in the final dispatch, for instance to distinguish between different types of terminals as in this example:

(defmulti funky-terminal #(if (number? %) :number (if (string? %) :string :other)))
(defmethod funky-terminal :other [terminal] (println "other: " terminal))
(defmethod funky-terminal :number [terminal] (println "number: " terminal))
(defmethod funky-terminal :string [terminal] (println "string: " terminal))
(binding [terminal funky-terminal]
  (traverse '(:a (:b 42 :x) "one")))

But that dispatch isn't the whole of the Visitor pattern, and frankly to hold it up as the Visitor pattern only demonstrates that you haven't grokked the complexity of dispatch from a complex data structure.

Consider another typical use of the Visitor pattern: traversal of graphs. Let's propose a traversal of a graph that is is guaranteed to reach all nodes and to traverse all edges while following edges through the graph. There are many graphs where this would require visiting some nodes and some edges multiple times. But there are many applications that would only want to give a special meaning to the first traversal of an edge or the first reaching of a node. There may even need to indicate the traversal has restarted discontinuously in a new part graph in those cases were there is not edge leading back from a node or where the graph is made of disconnected sub-graphs. Nothing in the node itself can tell you that this is the first or a subsequent reaching or traversal, much less that the traversal has had to become discontinuous with regard to following edges, (maybe you could infer that from reaching a node without passing an edge, maybe you could track nodes and edges, but why do all that work when a good visitor will tell you directly by calling a suitably named functions; if we're going to enjoy functional programming, lets not be ashamed to use functions in simple direct ways). All these different function calls represent different targets of dynamic binding in the model I've presented her. Of course you could use multi-methods to achieve dynamic dispatch if there are different types of nodes for example. But it would still be the Visitor pattern if there was only one type of node and no reason for dynamic dispatch.

Dynamic dispatch is a powerful feature in Clojure, but trying to justify it as a giant killer to the Visitor pattern shows a lack of understanding of Visitor pattern, and weakens the argument. Present the languages strengths, not your own weaknesses.