<br><br><div class="gmail_quote">On Wed, Sep 15, 2010 at 18:29, Robby Findler <span dir="ltr"><<a href="mailto:robby@eecs.northwestern.edu">robby@eecs.northwestern.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
I must admit, what you've done is quite the clever hack. You're<br>
loading the same file both at compile and and at runtime. At compile<br>
time you look at the exports using the (normally) runtime inspection<br>
facilities of the class system and use that to guide how compilation<br>
then happens of the module that interacts with the same file at<br>
runtime.<br></blockquote><div><br>This idea is in fact directly inspired by a runtime-path issue where Sam<br>told me to put some function definition in a separate module in import<br>it both with `require' and `require (for-syntax ....)'.<br>
("define-runtime-path-list and find-files" thread, July 5th)<br>Reasoning by analogy, however shallow sometimes, can sometimes be <br>of great help ;)<br><br> </div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
This hack will have limitations, </blockquote><div><br>Do you have specific ones in mind?<br> </div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
but you could take this one step<br>
further by having the argument to your macro be an identifier and then<br>
taking the symbolic name of the identifier and passing that to<br>
dynamic-require (at compile time) to get the actual class you want.<br></blockquote><div><br>That is a nice idea, I'll try that.<br> </div><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
<br>
If you can change the file that defines a%, you can make it export a<br>
kind of "table of contents" and then iterate over that too.<br></blockquote><div><br>You mean over exported classes? <br><br><br><br>Btw, here is an improvement of the code:<br>Now only a single call to define-class->singleton is sufficient, which makes<br>
things simpler.<br><br>#lang racket<br><br>(require (for-syntax racket/class))<br><br>;; Defines func as a sender of method to param-obj (parameter object).<br>;; Helper for define-class->singleton.<br>(define-syntax-rule (define-singleton-sender func param-obj method)<br>
(begin ;(printf "define-sender: ~s ~s ~s\n" 'func 'obj 'meth)<br> (define (func . args)<br> (send/apply (param-obj) method args))))<br><br>#|<br>Defines a function for each public method of class%,<br>
called on the default object held by the current-obj-id parameter.<br><br>class%: a class, which must be available both at runtime (require) <br> and at compile time (require for-syntax).<br>current-obj-id: an id that will be defined as a parameter <br>
which initial value is current-obj-exp.<br><br>Remarks:<br>- class% must be available at runtime (for normal use) and at compile-time<br>(for inspection of the method names and definition of functions).<br>- The "internal" class->singleton macro is necessary so that it can be<br>
abstracted over class%, because it cannot be an argument to the call<br>to class->singleton.<br>(see "Help: Passing a class to a macro and not an identifier", Sept. 15th.)<br>- Another tricky part is the #'id for datum->syntax: it is not possible to<br>
directly use one of the parameter of the top macro instead because<br>they exist at compile time, so that would define the names for compile time.<br>But passing current-obj-id to class->singleton will define an id at runtime,<br>
and the function names are then defined in the right phase.<br>Maybe there is a better way to do that.<br>|#<br>(define-syntax-rule (define-class->singleton class% current-obj-id current-obj-exp)<br> (begin<br> (define-syntax (class->singleton stx)<br>
(syntax-case stx ()<br> [(_ id)<br> (with-syntax ([(name (... ...))<br> ; #'id: names are defined for runtime<br> (map (λ(n)(datum->syntax #'id n)) <br>
(interface->method-names (class->interface class%)))])<br> #'(begin (define-singleton-sender name current-obj-id name)<br> (... ...)))]))<br> (define current-obj-id (make-parameter current-obj-exp))<br>
(class->singleton current-obj-id) ; pass a runtime identifier<br> ))<br><br> <br> <br><br>#| Tests |#<br><br>#| file "a.rkt":<br>#lang racket<br><br>(provide a% b%)<br><br>(define a%<br> (class object% (super-new)<br>
(init-field val)<br> <br> (define/public (get-the-val)<br> val)<br> ))<br><br>(define b%<br> (class object% (super-new)<br> (init-field valb)<br> <br> (define/public (get-the-valb)<br> valb)<br>
))<br>|#<br><br>(require "a.rkt"<br> (for-syntax "a.rkt"))<br><br>; Instantiate the macro for a% :<br>(define-class->singleton a% current-a (new a% [val 3]))<br><br><br>(get-the-val)<br>
<br>(parameterize ([current-a (new a% [val 5])])<br> (get-the-val))<br><br>(get-the-val)<br><br>(define-class->singleton b% current-b (new b% [valb "b"]))<br>(get-the-valb)<br><br>;|#<br></div></div>