Transparent Quotes

Hi, Racket Discourse.

This is something I've looked at before, in reference to @notjack's name and name-string under the private modules for Rebellion.

I thought about it again today while using C#'s nameof keyword and decided I'd try to figure it out in Racket because I bounced off the idea the last time around.

The part that was befuddling to me, was how to track the identifier as a literal (which I now say with the benefit of hindsight), given that we want to quote it, and that generally cuts it off from the rest of the context.

We can do that using the syntax-parse-state-cons! procedure, which allows one to manually update the state of syntax-parse. This affords finer-grained control over the info attached to certain syntax.

It so turns out, after reading static-name.rkt more carefully--and trying to run the bits in isolation--that the consequential snippet was (syntax-parse-state-cons! 'literals id). To wit, from the docs:

In addition, syntax-parse automatically adds identifiers that match literals (from ~literal patterns and literals declared with #:literals, but not from ~datum or #:datum-literals) under the key 'literals.

Anyways, the resulting code is nice and simple:

#lang racket/base

(require
  (for-syntax
   racket/base)
  racket/symbol
  syntax/parse/define)

(define-syntax-parse-rule (quote/literal id)
  #:do [(syntax-parse-state-cons! 'literals #'id)]
  (quote id))

(define-syntax-parse-rule (symbolof id:id)
  #:fail-unless (identifier-binding #'id)
  "expected a bound identifier"
  (quote/literal id))

(define-syntax-parse-rule (stringof id:id)
  #:fail-unless (identifier-binding #'id)
  "expected a bound identifier"
  (symbol->immutable-string (quote/literal id)))

(define (foo arg) (stringof foo))
(define bar 4)

(foo 'ignored)
(symbolof bar)
(stringof bar)
"foo"
'bar
"bar"
>

I am ambivalent about the names, specifically, but at least they do what they say on the tin.

Mooiloop.

5 Likes