[plt-scheme] Implementation of define-struct
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