Custom Reserved Identifiers
Written by Dominik Pantůček on 2026-01-29
racketLanguage-oriented programming has many nice features but some of them seem to be more versatile than others upon detailed inspection. This time we look into retrieving values from transformer bindings in syntax phase to create custom reserved identifiers.
Before we start, it is a good idea to write down what we are trying to achieve. Our
goal is to use certain identifiers only in certain context and it should be an error
everywhere else. In the following example we want to define entities r0
and r1 to evaluate to values 0 and 1
respectively in certain context but to be an invalid syntax otherwise.
(define-entity r0 0)
(define-entity r1 1)
(use-entity r0) ; OK
(use-entity 0) ; Error
(+ r0 r1) ; Error
The last part is probably the easiest. Whenever the identifier is used, we raise an error. A solution is rather simple with identifier transformers:
(define-syntax r0 0)
(define-syntax r1 1)
(+ r0 r1)
As we can see, it correctly does not allow such usage. So for the sake of simplicity we wrap such definition into a custom syntax using the infamous syntax-parse transformer:
(define-syntax (define-entity stx)
(syntax-parse stx
((_ id val)
#'(define-syntax id val))))
Not much improvement, but it will become handy soon.
But how to use such entity? It is actually rather simple, we just unwrap the value using a custom syntax again:
(define-syntax (use-entity stx)
(syntax-parse stx
((_ id:id)
#:with val (datum->syntax stx (syntax-local-value #'id))
#'val)))
It seems like we are done. But not completely, still the following would work and it should not:
(define r2 2)
(use-entity r2)
To tackle this problem a slightly more complex approach is needed. The transformer
binding will not be directly to a value but to a special struct that will allow the
use-entity syntax to check whether given identifier has the right
structure type attached to it.
(begin-for-syntax
(struct entity (val)))
(define-syntax (define-entity stx)
(syntax-parse stx
((_ id val)
#'(define-syntax id (entity val)))))
This way we have altered the resulting value of the binding to be wrapped in the
entity struct. This allows us to define a syntax
class for matching such bindings using the simple struct entity?
predicate:
(begin-for-syntax
(define-syntax-class entity
#:description "an entity"
(pattern e:id
#:do ((define v (syntax-local-value #'e (lambda () #f))))
#:when (entity? v))))
And with this syntax class available, it is rather simple to adjust the
use-entity syntax to check for only identifiers which are our newly
defined entities. Also the #:decsription ensures that when the compiler
complains in cases like (use-entity 2), it will tell the user that it is
"expecting an entity":
(define-syntax (use-entity stx)
(syntax-parse stx
((_ id:entity
#:with val (datum->syntax
stx
(entity-val (syntax-local-value #'id)))
#'val))))
Now all the requirements are met.
Hope you liked this little venture into the realm of less known types of transformer bindings and see ya next time for more!