On this page:
4.1 Putting it all together

4 Implementation of ADTs: syntax scopes

Due to TR bug #399, structs declared by a macro do not work if the macro itself is declared in a separate module. This seems to be due to the extra scope added as pieces of syntax cross the module boundary. There is unfortunately no equivalent to syntax-local-introduce that could be used to flip this module scope.

We therefore require the user to call (set-adt-context) at the beginning of the file. This macro stores the scopes present where it was called in a mutable for-syntax variable:

(define-for-syntax mutable-adt-context (box #f))

These scopes are later used as the context for struct identifiers:

(define-for-syntax (ctx-introduce id)
  (unless (unbox mutable-adt-context)
    (raise-syntax-error 'adt
                        (~a "(adt-init) must be called in the"
                            " file (or REPL). ") id))
   (replace-context (syntax-local-introduce
                     (unbox mutable-adt-context))

The (set-adt-context) macro should be called at the beginning of the file or typed in the REPL before using structures. It simply stores the syntax used to call it in mutable-adt-context.

(define-for-syntax (set-adt-context ctx)
  (set-box! mutable-adt-context ctx))
(define-syntax (set-adt-context-macro stx)
  (syntax-case stx ()
    [(_ ctx)
     (begin (set-box! mutable-adt-context #'ctx)

For debugging purposes, we provide a macro and a for-syntax function which show the current ADT context (i.e. the list of scopes).

(define-for-syntax (debug-show-adt-context)
   (hash-ref (syntax-debug-info (unbox mutable-adt-context))
(define-syntax (debug-show-adt-context-macro stx)
  #'(define dummy (void)))

The struct identifiers are introduced in a fresh scope Due to TR bug #399, this feature is temporarily disabled, until the bug is fixed., so that they do not conflict with any other user value.

(define-for-syntax struct-identifier-fresh-introducer
  (λ (x) x))

We provide two ways of checking whether set-adt-context was called: (adt-context?) returns a boolean, while (check-adt-context) raises an error when set-adt-context has not been called.

(define-for-syntax (adt-context?)
  (true? (unbox mutable-adt-context)))

(define-for-syntax (check-adt-context)
  (unless (adt-context?)
    (raise-syntax-error 'phc-adt
                         "adt-init must be called before"
                         " using the features in phc-adt"))))

4.1 Putting it all together

«*» ::=
  (require (for-syntax racket/base
(provide (for-syntax set-adt-context)
  (provide debug-show-adt-context