Thursday, August 12, 2010

Partially Implemented Protocols

Last time I discussed using a hash maps to improve code reuse with protocols. While we produced a set of good solutions, I couldn't help but feel like we aren't quite there. There still is a fair amount of chatter on the list about how to implement a default protocol, for example.

I started experimenting with an idea I'm calling a Partially Implemented Protocol (PIP). The idea is that you can use a PIP like you would a BaseClass in Java. After playing around for a little bit, I settled on the following solution:



The two major components is the PIP record and extend+ macro. Let me re-write yesterday's example with the new expand+ syntax.

(defprotocol UIProtocol
 (render [])
 (action [evt])
 (button-down [evt])
 (button-up [evt]))

(def BaseUIProtocol
 (pip UIProtocol
 {:action (fn [this evt] ...)
   :button-down (fn ([this evt]...)
   :button-up (fn ([this evt]...)})

(extend+ TerranUIType
  BaseUIProtocol
  {:render an-existing-fn})

(extend+ ProtossUIType
  BaseUIProtocol
  {:render a-different-fn})

What I really like about the use of a PIP is that it allows reference to the protocol & partial implementation with one record. You can get at the internal simply by using keyword lookup, and you can also derive a PIP from another PIP like so:

(def up-down-override
  :button-down button-down-2
  :button-up button-up-2})

(extend+ ZergUIType
  (pip BaseUIProtocol up-down-override)
  {:render a-buggy-fn})

I'm also working on versions of reify+ proxy+, etc. that all work with PIPs, assuming this idea is worth exploring.

No comments:

Post a Comment