[plt-scheme] Dot-notation for structure field access

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Mon Mar 19 02:56:01 EDT 2007

At Sun, 11 Mar 2007 12:49:17 +0100, "jos koot" wrote:
> Inspired by the dot notation of Jens Axel Søgaard I tried to eliminate the 
> need for define-accessor. See enclosure. However, I can't get rid of the error 
> reported at the end of my trial, reading:
> "compile: bad syntax; reference to top-level identifier is not allowed, 
> because no #%top syntax transformer is bound in: acc85"

The problem starts here:

 (define-syntax (define-struct-type stx)
  (syntax-case stx ()
   ....
   ((define-struct-type (descr constr pred super) (field ...) inspector)
    (let-values (((acc mut) (apply values (generate-temporaries #'(acc mut)))))
     (register-transformer-builder #'constr #'(field ...) acc mut)
   #`(define-values (descr constr pred #,acc #,mut)
       ....)))))

The call to `register-transformer-builder' is a side effect that
happens while the macro is expanded. After a `define-struct-type' form
is expanded --- say, when compiling a module --- the side-effect won't
happen anymore. In particular, the side effect won't happen if you
(next week, on a different machine) load the compiled form of module
whose source contains the `define-struct-type' declaration.

Well, the error you see is only indirectly related to the problem,
which is partly why it's difficult to track down. The problem is that
the registration uses `acc' and `mut' identifiers before any such
bindings exist, and that leads to the bad reference.

The solution to both problems is the same, and it's simple to write
down:

 (define-syntax (define-struct-type stx)
  (syntax-case stx ()
   ....
   ((define-struct-type (descr constr pred super) (field ...) inspector)
    (let-values (((acc mut) (apply values (generate-temporaries #'(acc mut)))))
     #`(begin
         (begin-for-syntax
          (register-transformer-builder #'constr #'(field ...) #'#,acc #'#,mut))
         (define-values (descr constr pred #,acc #,mut)
           ....))))))

All I've done is move the `register-transformer-builder' call into the
result of the macro, so that it's a side-effect wrapped by
`begin-for-syntax'. Since it's part of the result expansion, the side
effect happens each time the expanded (or compiled) expression is
loaded.

Moreover, `acc' and `mut are now in the same binding context as the
`define-values' form, so they get bound in the way that you want.


The general rule is: Don't put any side-effects in a macro expansion.
If you must have side effects at compile time, they should be in
`begin-for-syntax'.


See also 
  http://www.cs.utah.edu/plt/publications/macromod.pdf
which may make more sense now that you've hit the problem that it
describes. :)


Unfortunately, there's a small catch, which I think is almost certainly
related to your later question about `syntax-recertify'. If you try

 (module m mzscheme
   (require dot)
   (define-struct-type d c p (x y z) (make-inspector))
   (provide d c p))

 (module n mzscheme
   (require dot m)
   (define-struct-var s (c 1 2 add1))
   (printf "~s\n" s.x))

then you get

 compile: access from an uncertified context to unexported variable
 from module: m at: acc1 in: acc1.1

Even though there's no `local-expand' in the macro implementation,
there is a kind of manual expansion that happens when looking up a
binding via `register-lookup'. That manual lookup must be accompanied
by manual management of certificates (though not through using
`syntax-recertify').

So, explicitly certify the referencing identifiers before you record
them:

 (define-syntax (define-struct-type stx)
  (syntax-case stx ()
   ....
   ((define-struct-type (descr constr pred super) (field ...) inspector)
    (let-values (((acc mut) (apply values (generate-temporaries #'(acc mut)))))
     #`(begin
         (begin-for-syntax
          (let ([cert (syntax-local-certifier)])
            (register-transformer-builder #'constr #'(field ...) 
                                          (cert #'#,acc)
                                          (cert #'#,mut))))
         (define-values (descr constr pred #,acc #,mut)
           ....))))))

Revised code and example enclosed.

Matthew
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: dot.scm
URL: <http://lists.racket-lang.org/users/archive/attachments/20070319/61197c27/attachment.ksh>

Posted on the users mailing list.