[racket] identifier used out of context error when making my own object-copy

From: Alexander D. Knauth (alexander at knauth.org)
Date: Sat Apr 19 10:45:53 EDT 2014

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 <greghendershott at 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).

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20140419/613b70be/attachment-0001.html>

Posted on the users mailing list.