[plt-dev] `struct' and `define-struct'

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Sun Apr 25 14:15:48 EDT 2010

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)

   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

   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

      > (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

   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
      > make-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.

Posted on the dev mailing list.