[plt-scheme] An interface wrapper using macros and interface->method-names

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Wed Jun 2 20:54:21 EDT 2004

On Jun 2, 2004, at 6:05 PM, David Stigant wrote:

> I've been trying to write a macro which will take an interface and 
> create a class which forwards all calls to the interface's methods on 
> to another implementation of that interface.  For example, given this 
> interface:
> (define ifoo<%> (interface () foo bar baz))

The above defines ifoo<%> to be a run-time value of kind interface.

>  I want my macro
> (wrap ifoo<%>)

This is trying to use a value, which doesn't exist yet, to create a 
piece of syntax. Once this piece of syntax is compiled and run will 
produce a value that co-exists with ifoo<%>.

You can turn interfaces into quasi-syntactic things and simultaneously 
create wrappers:

(require-for-syntax (lib "class.ss"))

(define-syntax (define-interface stx)
   (syntax-case stx ()
     [(_ iname (super-name ...) name ...)
      (let ([iname-wraped
               (string-append (symbol->string (syntax-e (syntax iname))) 
        #`(define-values (iname #,iname-wraped)
            (let* ([ii (interface (super-name ...) name ...)]
                   [cc (class* object% (ii)
                         (init-field (implementation #f))
                         (define/public (switch-implementation i)
                           ;; after some basic checks
                           (set! implementation i))
                         (define/public name (lambda (x) (send 
implementation name x)))
              (values ii cc))))]))

(define-interface ifoo () foo bar baz)

(define mumble (class object% (super-new) (define/public (foo x) 10)))

(define fake (new ifoo-wrapped (implementation (new mumble))))

(send fake foo 22)

; (send fake moo 33)

NOTE: I don't understand why the class* inside the macro doesn't signal 
an error for not satisfying the interface.

-- Matthias

> to expand to this:
> (class* object% (ifoo<%>)
>    (init-field (impl void))
>    (public*
>       (foo (lambda args (send impl foo . args)))
>       (bar (lambda args (send impl bar . args)))
>       (baz (lambda args (send impl baz . args)))
>       (set-impl (lambda (i) (set! impl i))))
>    (super-instantiate ())
> The first three methods of this class simply forward requests to the 
> ifoo<%> interface onto impl.  The set-impl method allows me to change 
> the implementation under the covers without any of the clients of my 
> object knowing or having to update their references to the new impl.
> Now, what I have started with is this:
> (define-syntax (wrap stx)
>    (syntax-case stx ()
>        ((wrap iface<%> (method ...))
>         #`(class* object% (iface<%>)
>              (init-field (impl void))
>              (public*
>                  (set-impl (lambda (i) (set! impl i)))
>                  (method (lambda args (send impl method . args))) ...)
>              (super-instantiate ())))))
> Which works great (ie it produces the correct class) except  that I 
> have to invoke the macro like this:
> (wrap ifoo<%> (foo bar baz))
> I don't want to have to list all the method names out everytime I use 
> this wrapper.  What if I change the method names in the interface?  
> Now I have to go back and change all the invocations of the macro as 
> well.  Lousy.  So I wanted to use the interface->method-names function 
> to generate the list of methods and then pass those into the macro 
> automatically.  For reference, interface->method-names works like 
> this:
> (interface->method-names ifoo<%>)   ==> '(foo bar baz)
> So it seems like I ought to be able to do this:
> (wrap ifoo<%> (interface->method-names ifoo<%>))
> However, this yeilds a class with methods "interface->method-names" 
> and "ifoo<%>" instead of "foo" "bar" and "baz".  Or rather, doesn't 
> produce anything since that class doesn't properly implement ifoo<%>
> What I've come up with is this hack:
> (define make-wrapper
>    (lambda (iface<%>)
>       (eval  ` (wrap iface<%> ,(interface->method-names iface<%>)))))
> (define foo-wrapper% (make-wrapper ifoo<%>))
> But my skin crawls whenever I use eval.  I've tried this out in local 
> and global contexts and it seems to work, but is it really kosher and 
> is there a cleaner way to accomplish what I want?  Is there a way to 
> combine the make-wrapper function into the macro definition?
> Any help is appreciated.
> Dave

Posted on the users mailing list.