[racket] Help: Passing a class to a macro and not an identifier

From: Laurent (laurent.orseau at gmail.com)
Date: Wed Sep 15 13:33:20 EDT 2010

On Wed, Sep 15, 2010 at 18:29, Robby Findler <robby at eecs.northwestern.edu>wrote:

> I must admit, what you've done is quite the clever hack. You're
> loading the same file both at compile and and at runtime. At compile
> time you look at the exports using the (normally) runtime inspection
> facilities of the class system and use that to guide how compilation
> then happens of the module that interacts with the same file at
> runtime.
>

This idea is in fact directly inspired by a runtime-path issue where Sam
told me to put some function definition in a separate module in import
it both with `require' and `require (for-syntax ....)'.
("define-runtime-path-list and find-files" thread, July 5th)
Reasoning by analogy, however shallow sometimes, can sometimes be
of great help ;)



> This hack will have limitations,


Do you have specific ones in mind?


> but you could take this one step
> further by having the argument to your macro be an identifier and then
> taking the symbolic name of the identifier and passing that to
> dynamic-require (at compile time) to get the actual class you want.
>

That is a nice idea, I'll try that.


>
> If you can change the file that defines a%, you can make it export a
> kind of "table of contents" and then iterate over that too.
>

You mean over exported classes?



Btw, here is an improvement of the code:
Now only a single call to define-class->singleton is sufficient, which makes
things simpler.

#lang racket

(require (for-syntax racket/class))

;; Defines func as a sender of method to param-obj (parameter object).
;; Helper for define-class->singleton.
(define-syntax-rule (define-singleton-sender func param-obj method)
  (begin ;(printf "define-sender: ~s ~s ~s\n" 'func 'obj 'meth)
         (define (func . args)
           (send/apply (param-obj) method args))))

#|
Defines a function for each public method of class%,
called on the default object held by the current-obj-id parameter.

class%: a class, which must be available both at runtime (require)
  and at compile time (require for-syntax).
current-obj-id: an id that will be defined as a parameter
  which initial value is current-obj-exp.

Remarks:
- class% must be available at runtime (for normal use) and at compile-time
(for inspection of the method names and definition of functions).
- The "internal" class->singleton macro is necessary so that it can be
abstracted over class%, because it cannot be an argument to the call
to class->singleton.
(see "Help: Passing a class to a macro and not an identifier", Sept. 15th.)
- Another tricky part is the #'id for datum->syntax: it is not possible to
directly use one of the parameter of the top macro instead because
they exist at compile time, so that would define the names for compile time.
But passing current-obj-id to class->singleton will define an id at runtime,
and the function names are then defined in the right phase.
Maybe there is a better way to do that.
|#
(define-syntax-rule (define-class->singleton class% current-obj-id
current-obj-exp)
  (begin
    (define-syntax (class->singleton stx)
      (syntax-case stx ()
        [(_ id)
         (with-syntax ([(name (... ...))
                        ; #'id: names are defined for runtime
                        (map (λ(n)(datum->syntax #'id n))
                             (interface->method-names (class->interface
class%)))])
           #'(begin (define-singleton-sender name current-obj-id name)
                    (... ...)))]))
    (define current-obj-id (make-parameter current-obj-exp))
    (class->singleton current-obj-id) ; pass a runtime identifier
    ))




#| Tests |#

#| file "a.rkt":
#lang racket

(provide a% b%)

(define a%
  (class object% (super-new)
    (init-field val)

    (define/public (get-the-val)
      val)
    ))

(define b%
  (class object% (super-new)
    (init-field valb)

    (define/public (get-the-valb)
      valb)
    ))
|#

(require "a.rkt"
         (for-syntax "a.rkt"))

; Instantiate the macro for a% :
(define-class->singleton a% current-a (new a% [val 3]))


(get-the-val)

(parameterize ([current-a (new a% [val 5])])
  (get-the-val))

(get-the-val)

(define-class->singleton b% current-b (new b% [valb "b"]))
(get-the-valb)

;|#
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20100915/e68299ea/attachment.html>

Posted on the users mailing list.