Monday, July 06, 2009

Macros and OO Programming

Earlier today I had a pretty typical OO problem. I had a single object which I wanted to replace with a collection of objects. If I had done my programming correctly, all I needed was a simple delegator that would pass on the top level method call to a list of objects. Effectively, turning:

 class Robot {
   void turn(degrees) { 
    // do the actual turning
   }
 }

Into:

 class Gang {
   void turn(degrees) {
     foreach(robot r) {
       r.turn(degrees);
     }
   }
 }

I could have hand written each method in such a delegator, but I'm too lazy for that.

If I were programming in Java and performance wasn't a concern, I'd probably make use of introspection to help get me part of the way there. If this were PHP, or any other language with magic methods, I could have just implemented the delegator as a dynamic function.

But, in this case I'm programming in PLT Scheme, so macros seemed like the right way to go. In no time, I whipped up:

(define-syntax delegator
  (syntax-rules ()
    [(_ (method ...) target ...)
     (make-object (class object%
                    (super-new)
                    (define targets (list target ...))
                    (define/public (method . args)
                      (for-each (lambda (t)
                                  (send/apply t method args))
                                targets))
                    ...))]))

It's used as follows:

 (define gang (delegator [turn forward] (new robot%) (new robot%) (new robot%)))

This reads as: create a new object gang which will delegate the methods turn and forward to the robot% instances passed into it.

I'm sure there are more advanced things this macro could do, but for my purposes, it worked quite well.

Of course, this is really nothing more than doing on the fly code generation effectively creating just the right class with just the right methods to delegate to. Which is what makes macros so valuable, they aren't some esoteric concept - they're simple: they are code that generates code for you. They should be, and often are, a programmer's best friend.

4 comments:

  1. Anonymous7:06 AM

    cool the scheme language

    ReplyDelete
  2. As a note. Your targets are static so it should be possible to expand them inline in the define/public.
    Something like

    (define/public (method . args)
    (send/apply target method args) ...)

    ReplyDelete
  3. IMHO, in a 00 way, a composite pattern would do the trick.
    Macros are funkier though :)

    ReplyDelete