On this page:
«adt-context»1
«ctx-introduce»
«adt-context»2
«adt-context»3
«fresh-introducer»
«adt-context?»
«check-adt-context»
4.1 Putting it all together
«*»
7.1

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))
  (struct-identifier-fresh-introducer
   (replace-context (syntax-local-introduce
                     (unbox mutable-adt-context))
                    id)))

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)
            #'(void))]))

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)
  (displayln
   (hash-ref (syntax-debug-info (unbox mutable-adt-context))
             'context)))
(define-syntax (debug-show-adt-context-macro stx)
  (debug-show-adt-context)
  #'(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
                        (string-append
                         "adt-init must be called before"
                         " using the features in phc-adt"))))

4.1 Putting it all together

«*» ::=
(begin
  (require (for-syntax racket/base
                       racket/syntax
                       racket/set
                       racket/list
                       racket/format
                       phc-toolkit/untyped
                       syntax/strip-context)
           racket/require-syntax
           type-expander
           phc-toolkit
           remember))
 
(provide (for-syntax set-adt-context)
         set-adt-context-macro
         debug-show-adt-context-macro)
 
(begin-for-syntax
  (provide debug-show-adt-context
           adt-context?
           check-adt-context
           ctx-introduce))
 
«adt-context»
«fresh-introducer»
«ctx-introduce»
«adt-context?»
«check-adt-context»