[plt-scheme] Implementation of define-struct

From: Luís Fernando Schultz Xavier da Silveira (lfsxs0 at gmail.com)
Date: Sun Jul 19 22:44:31 EDT 2009

Thank you very, very much for the help. It was really useful and I
really appreciated it.

On Sun, Jul 19, 2009 at 08:26:11PM -0400, Ryan Culpepper wrote:
> Luís Fernando Schultz Xavier da Silveira wrote:
>> I have a very simple (and probably stupid) question I can't figure
>> out. Assuming the define-struct feature is not a core form, how is it
>> it defines names based on a macro argument?
>> For instance, (define-struct point (x y)) generates point?, make-point,
>> point-x and point-y. But how exactly is it I can create a macro that
>> will define bindings whose names are derived from its arguments?
>
> It cannot be done with just 'syntax-rules' macros. You need to be able to 
> work with the syntax objects (the representation of programs, macro 
> arguments, etc) directly. An introduction to general (or "procedural") 
> macro transformers is here:
>
> http://docs.plt-scheme.org/guide/proc-macros.html
>
> --
>
> Here's a similar, but simpler, macro that defines other names not given as 
> arguments to the macro but based on an argument to the macro:
>
>   ;; (define/get+set name expr) defines 'name' as a variable,
>   ;; 'get-name' as a procedure that returns the variable's value,
>   ;; and 'set-name!' as a procedure that updates the variable
>   ;; with a new value.
>
> First, this requires programming "for syntax", so:
>
>   (require (for-syntax scheme/base))
>
> Second, this task requires constructing new symbols, so here's a helpful 
> function. It is a *compile-time* auxiliary function; if the 
> 'begin-for-syntax' were deleted, it would be a *run-time* function, and it 
> couldn't be used inside of the macro. (See "Compile and Run-Time Phases" 
> for more information.)
>
>   (begin-for-syntax
>     ;; format-symbol : format-string any ... -> symbol
>     (define (format-symbol fmt . args)
>       (string->symbol (apply format fmt args))))
>
> Next thing is to write auxiliaries that construct the new identifiers you 
> want to introduce. An identifier is just a syntax object containing a 
> symbol; that gives you a way to create new ones based on old ones.
>
>   (begin-for-syntax
>     ;; make-getter : identifier -> identifier
>     (define (make-getter name)
>       (datum->syntax name (format-symbol "get-~a" (syntax-e name))))
>     ;; make-setter : identifier -> identifier
>     (define (make-setter name)
>       (datum->syntax name (format-symbol "set-~a!" (syntax-e name)))))
>
> Above, 'syntax-e' extracts the symbol. After the new symbol is constructed, 
> 'datum->syntax' wraps it up again as a syntax object. There is important 
> extra information that goes in a syntax object. In this case, 
> 'datum->syntax' transfers that information from its first argument, the 
> original identifier.
>
> Here's the rest of the macro:
>
>   (define-syntax (define/get+set stx)
>     (syntax-case stx ()
>       [(define/get+set name value)
>        (with-syntax ([getter (make-getter #'name)]
>                      [setter (make-setter #'name)])
>          #'(begin (define name value)
>                   (define (getter) name)
>                   (define (setter new-value)
>                     (set! name new-value))))]))
>
> Try it out:
>
> > (define/get+set foo 12)
> > (get-foo)
> 12
> > (set-foo! 34)
> > (get-foo)
> 34
> > foo
> 34
>
> Ryan


Posted on the users mailing list.