[racket] Binding undefined with a macro

From: J. Ian Johnson (ianj at ccs.neu.edu)
Date: Tue Sep 3 13:21:49 EDT 2013

Ah yes, this is an annoying part of structs. The definition of the struct-type also takes in the properties it will have, so the accessors are not defined at the point of property definition. If you use the identifier without eta-expanding it (i.e. if foo takes two arguments, its eta-expansion is (lambda (x y) (foo x y))), then Racket will try to evaluate it, but it's not defined yet -- oops.

A way to get around this, since you know it's a field accessor, is to generate a temporary name that is the eta-expansion, and use that in the prop:inners.

(define-syntax (struct-abc stx)
    (syntax-case stx ()
      [(_ sname (name args ...) props ...)
       (with-syntax* ([sname-name (format-id #'sname "~a-~a" #'sname #'name)]
                      [temp (generate-temporary #'sname-name])
       #`(begin
          (struct sname (name args ...) props ...
           #:property prop:insiders temp)
          (define (temp x) (sname-name x))))]))

-Ian

----- Original Message -----
From: "antoine" <antoine597 at gmail.com>
Cc: users at racket-lang.org
Sent: Tuesday, September 3, 2013 1:17:40 PM GMT -05:00 US/Canada Eastern
Subject: Re: [racket] Binding undefined with a macro

I have grasp the problem i have expanded the file and narrow the region 
concerned and get :

(define-values
     (struct:person person1 person? person-name person-nickname) ; 
define here
     (let-values (((struct: make- ? -ref -set!)
                   (let-values ()
                     (let-values ()
                       (#%app
                        make-struct-type
                        'person
                        '#f
                        '2
                        '0
                        '#f
                        (#%app list (#%app cons prop:insiders 
person-name)) ; accessed here
                        (#%app current-inspector)
                        '#f
                        '(0 1)
                        '#f
                        'person)))))
       (#%app
        values
        struct:
        make-
        ?
        (#%app make-struct-field-accessor -ref '0 'name)
        (#%app make-struct-field-accessor -ref '1 'nickname))))

We see that person-name is accessed before it definition.

So i rewrite the macro :

(define-syntax (struct-abc stx)
    (syntax-case stx ()
      [(_ sname (name args ...) props ...)
       (with-syntax ([sname-name (format-id #'sname "~a-~a" #'sname 
#'name)])
       #`(struct sname (name args ...) props ...
           #:property prop:insiders sname-name))]))

to :

(define-syntax (struct-abc stx)
    (syntax-case stx ()
      [(_ sname (name args ...) props ...)
       (with-syntax ([sname-name (format-id #'sname "~a-~a" #'sname 
#'name)])
       #`(struct sname (name args ...) props ...
           #:property prop:insiders (lambda () sname-name)))]))


I don't know if there is another way to do this sort of recursive 
binding without a lambda.

Thanks for you reply it helps me point out the problem.

On 03/09/2013 18:36, J. Ian Johnson wrote:
> The struct form generates names unhygienically, but predictably. You will have to have a handle on both the struct name and field name to produce an identifier equal to the generated field accessor.
> In your case, if you can commit to name always being given first (there are ways around this that involve more parsing), then the following will allow you to write your macro correctly:
>
> (require (for-syntax racket/syntax))
> (define-syntax (struct-abc stx)
>     (syntax-case stx ()
>       [(_ sname (name args ...) props ...)
>        (with-syntax ([sname-name (format-id #'sname "~a-~a" #'sname #'name)])
>        #`(begin
>        (+ 1 1)
>        (struct sname (name args ...) props ...
>            #:property prop:insiders sname-name)))]))
>
> -Ian
> ----- Original Message -----
> From: "antoine" <antoine597 at gmail.com>
> To: users at racket-lang.org
> Sent: Tuesday, September 3, 2013 12:23:17 PM GMT -05:00 US/Canada Eastern
> Subject: [racket] Binding undefined with a macro
>
> Hello,
>
> With this macro:
>
> (define-syntax (struct-abc stx)
>     (syntax-case stx ()
>       [(_ name (args ...) props ...)
>        #`(begin
>        (+ 1 1)
>        (struct name (args ...) props ...
>            #:property prop:insiders person-name))]))
>
> (define-values (prop:insiders insiders? insiders-ref)
>     (make-struct-type-property 'insider))
>
>
> When i do :
>
> (struct-abc person (name nickname))
>
> I get :
>
> person-name: undefined;
>    cannot reference an identifier before its definition
>
> But i know that person-name is defined the problem come from 'begin' in
> the macro, if i rewrite like this :
>
> (define-syntax (struct-abc stx)
>     (syntax-case stx ()
>       [(_ name (args ...) props ...)
>        #`(struct name (args ...) props ...
>            #:property prop:insiders person-name)]))
>
> It works as expected.
>
> So my question are:
>
> Could you explain (point out) me why the person-name is unknown at this
> point?
> And how can i change the begin with something like '#,@'?
>
> Thank you.
> ____________________
>    Racket Users list:
>    http://lists.racket-lang.org/users

____________________
  Racket Users list:
  http://lists.racket-lang.org/users

Posted on the users mailing list.