[plt-dev] `struct' and `define-struct'
Updating documentation has forced me to think a lot more about
`struct', `define-struct', and the transition path for `scheme' to
`racket', and so I've made some changes:
* `define-struct' (as provided by both `scheme/base' and
`racket/base') binds the type name as a constructor in addition to
the `make-'-prefixed name.
> (define-struct a (x y))
> (a 1 2)
#<a>
Furthermore, the name of the constructor as reported by
`object-name' is the type name, not the `make-'-prefixed name:
> (define-struct a (x y))
> make-a
#<procedure:a>
This makes our existing libraries `racket'-ready without having to
change all the `define-struct's to `struct'. And since the type name
bound by `define-struct' was previously disallowed as an expression,
the change is generally backward-compatible.
* The `struct' and `define-struct' forms accept `#:constructor-name'
and `#:extra-constructor-name' options.
The `#:constructor-name' option prevents the type name from working
as a constructor (unless the supplied name matches the type name).
Also, `#:constructor-name' makes the reflective name of the
constructor (as reported by `object-name', for example) the symbolic
form of the given constructor name, instead of the symbolic form of
the type name.
The `#:extra-constructor-name' option adds a constructor binding
while keeping the type name as a constructor and preserving the
symbolic type name as the reflective name of the constructor.
The old `define-struct' form (and the form in the HtDP languages)
corresponds to use `struct' with a `make-'-prefixed name as
`#:constructor-name':
> (define-struct a (x y) #:constructor-name make-a)
> a
a: identifier for static struct-type information cannot be used
as an expression in: a
> make-a
#<procedure:make-a>
The new `define-struct' form, in contrast, corresponds to use
`struct' with a `make-'-prefixed name as `#:extra-constructor-name':
> (define-struct a (x y) #:extra-constructor-name make-a)
> a
#<procedure:a>
> make-a
#<procedure:a>
This use of `#:extra-constructor-name' above is different from
> (define-struct a (x y))
> (define make-a a)
in a subtle but crucial way: When you bind an extra constructor via
`#:extra-constructor-name', the extra constructor is recorded as the
constructor name in the expand-time binding of the type name, which
means that `struct-out' exports the extra constructor.
* In Scribble sources, `defstruct' corresponds to `define-struct', but
it's rendered using `struct' and `#:extra-constructor-name'. A new
`defstruct*' corresponds to `struct'.
I don't like having the starred form be the preferred form while the
unstarred one is is for backward compatibility, but I couldn't find
a better name. I hope that a future `scheme/manual' replacement will
clean this up (along with many other awkward names an
inconsistencies in the Scribble libraries for writing manuals).
* Not a change, but a detail to keep in mind: The `scheme' language
provides `struct' for signatures via `scheme/unit', which is
different from the `struct' in `racket' that is also recognized in
signatures. The `mzlib/unit' library provides an even older and
different `struct' for signatures.
The `struct' from `scheme/unit' is like `define-struct', in that a
`#:extra-constructor-name' option with ` make-' prefix is implicit.
Whether you use `struct' from `scheme/unit' or from `racket' in a
signature, the `#:constructor-name' and `#:extra-constructor-name'
options are supported.