For some reason I tried adding a (with-syntax ([%-stx %] [obj-stx obj]) …) and it actually helped.
I have no idea why you would need to do that though. #lang racket/base (require (for-syntax racket/base)) (provide object-copy) (require racket/class (for-syntax syntax/parse)) (module+ test (require rackunit)) (define-syntax object-copy (lambda (stx) (syntax-parse stx [(object-copy %-expr obj-expr [field-id:id expr:expr] ...) #:declare %-expr (expr/c #'class? #:name "class") #:declare obj-expr (expr/c #'(is-a?/c %-expr) #:name "object") (with-syntax ([ooo '...]) #'(let ([% %-expr.c] [obj obj-expr.c]) (define-values (name field-cnt all-field-syms field-accessor field-mutator super-class skipped?) (class-info %)) (define remaining-field-syms (remove* '(field-id ...) all-field-syms)) (with-syntax ([%-stx %] [obj-stx obj] [(remaining-field-id ooo) remaining-field-syms]) (eval-syntax #'(new %-stx [field-id expr] ... [remaining-field-id (get-field remaining-field-id obj-stx)] ooo )))))] ))) (module+ test (define fish% (class object% (init-field color weight) (super-new) (inspect #f))) (define marlin (new fish% [color 'orange-and-white] [weight 11])) (define dory (object-copy fish% marlin [color 'blue])) (check-equal? dory (new fish% [color 'blue] [weight 11])) (define shark% (class fish% (init-field weeks-since-eating-fish) (super-new) (inspect #f))) (define bruce (new shark% [color 'grey] [weight 110] [weeks-since-eating-fish 3])) (define chum (object-copy shark% bruce [weight 90] [weeks-since-eating-fish 0])) (check-equal? chum (new shark% [color 'grey] [weight 90] [weeks-since-eating-fish 0])) (define not-really-chum (object-copy fish% bruce [weight 90])) (check-equal? not-really-chum (new fish% [color 'grey] [weight 90])) ) On Apr 18, 2014, at 11:45 PM, Greg Hendershott <greghendersh...@gmail.com> wrote: > Although I have hardly any experience with racket/class, and I'm not > very knowledgeable about how Racket structs are implemented, this got > me curious to look at how struct-copy is implemented. > > First, your macro appears to be trying to work around the problem that > `get-field-names` only makes sense at runtime (when given some > particular object at runtime) -- but your macro is at compile-time. > That's probably why you tried the nested `(with-syntax #'( > (with-syntax (eval-syntax #'( )))))`. > >> And also, is there another version of field-names that takes a class instead >> of an object, because I think that will create another problem when I get to >> the last test (with not-really-chum). > > `class-info` looks promising for that. If only you could get a > `class?` at compile time. But I haven't figured out how. > > Racket struct identifiers have a meaning both at run time (as a > constructor) and at compile time (as a transformer binding to struct > info): > > "5.7 Structure Type Transformer Binding > > The struct form binds the name of a structure type as a transformer > binding that records the other identifiers bound to the structure > type, the constructor procedure, the predicate procedure, and the > field accessor and mutator procedures. This information can be used > during the expansion of other expressions via syntax-local-value." > > As a result, struct-copy can use syntax-local-value to get the > compile-time value, and pass it to extract-struct-info, to get a list > of field identifiers. > > But unfortunately a class identifier does not seem to have any > syntax-local-value at all (much less one that you could pass to > class-info). > > That's my attempt at figuring it out. Hopefully one of the Racket > experts can give you an actual solution (and/or correct any of what I > wrote here).
____________________ Racket Users list: http://lists.racket-lang.org/users