[racket] dynamically auto-initialized private struct fields

From: David T. Pierson (dtp at mindstory.com)
Date: Sat Nov 9 12:50:03 EST 2013

Hi all,

Sometimes when defining struct types it is useful to have fields which
are essentially "private" to the module (fields whose accessors/mutators
are not exported from the module.) I want these fields to be initialized
automatically with arbitrary values generated at construction time.

Here are some methods I've considered and what I don't like about them:

1) Create a wrapper around the generated constructor which provides the
correct values for the private fields:

(provide make-mystruct1
         mystruct1-public)
(struct mystruct1 (public private))
(define (make-mystruct1 public)
  (mystruct1 public (get-new-private)))

The downside of this method is that you can't allow arbitrary subtypes
outside the module because they'd have to supply an invalid value for
the private field.

2) To fix the subtype constructor problem, we could make the fields
#:auto and provide a separate init procedure:

(provide mystruct2
         make-mystruct2
         init-mystruct2
         mystruct2-public)
(struct mystruct2 (public [private #:auto])
        #:constructor-name make-mystruct2
        #:mutable)
(define (init-mystruct2 m2)
  (set-mystruct2-private! m2 (get-new-private)))

Now subtype constructors need not specify a bogus value for the private
field, but the init procedure is not elegant.  (Wrappers could be
defined for each type/subtype constructor which also call the init
procedure.)

3) Specify a guard procedure:

(provide mystruct3
         make-mystruct3
         mystruct3-public)
(struct mystruct3 (public private)
        #:constructor-name make-mystruct3
        #:guard (lambda (public private-ignored name)
                  (values public (get-new-private))))

Here the guard procedure provides a valid value for the private field,
but unfortunately the constructor and subtype constructors still need to
take a bogus value for the private field (which will be ignored in the
guard.)  Like the init procedure method, that could be fixed by having a
separate wrapper for each type/subtype constructor.

Have I missed a method?

What I'd really like is to be able to specify the private fields as
#:auto so they need not be given as constructor arguments but still have
a guard procedure return them.  Something like:

(provide mystruct5
         make-mystruct5
         mystruct5-public)
(struct mystruct5 (public [private #:auto])
        #:constructor-name make-mystruct5
        #:guard* (lambda (public name)
                  (values public (get-new-private))))

This would allow me to completely control the private field within my
module while not requiring bogus/ignored constructor arguments nor
requiring wrappers.

(If there is nothing like this currently, and others think it would be
good to have, I'd be willing to take a stab at implementing it.)

Thanks.

David


Posted on the users mailing list.