Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Temporay Identifiers On-the-fly #22

Open
shhyou opened this issue Sep 2, 2021 · 2 comments
Open

Generate Temporay Identifiers On-the-fly #22

shhyou opened this issue Sep 2, 2021 · 2 comments

Comments

@shhyou
Copy link

shhyou commented Sep 2, 2021

Macro

Some macros need to generate a sequence of fresh identifiers corresponding to a list of input forms. The standard solution is invoking generate-temporaries with a syntax list and bind the result to a new pattern variable. However, the new pattern variable is disconnected from the input forms and such an approach quickly becomes unmanageable when the input forms come nested in more than one ellipses.

By delegating the work to syntax classes, the syntax attributes tightly couples the generate identifiers with the input forms (and they even have DrRacket binding arrows). Moreover, repetition handling is entirely done by syntax/parse, thereby simplifying the code.

#lang racket/base

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

(provide (for-syntax fresh-variable))

(begin-for-syntax
  ;; I'm not sure if `context` is ever going to be useful but I added it anyway.
  (define-syntax-class (fresh-variable [context #f])
    #:attributes (fresh-var)
    (pattern name
             #:with temp-var (generate-temporary #'name)
             #:with fresh-var (if context
                                  (format-id context "~a" #'temp-var)
                                  #'temp-var))))

Example

In this example, we create a define/immutable-parameter form for defining function while disabling mutation of the parameters through make-variable-like-transformer.

The macro define/immutable-parameter parses the arguments arg with fresh-variable to generate temporary identifiers on-the-fly. Each of the fresh identifier is directly paired with the original identifier through syntax attributes.

(require (for-syntax syntax/transformer))
(define-syntax (define/immutable-parameter stx)
  (syntax-parse stx
    [(_ (name:id (~or* (~and _:id arg:fresh-variable)
                       [(~and _:id arg:fresh-variable) default-value:expr])
                 ...)
        body:expr ...+)
     #'(define (name (~? [arg.fresh-var default-value]
                         arg.fresh-var) ...)
         ;; disable set! on arg
         (define-syntax arg
           (make-variable-like-transformer #'arg.fresh-var #f))
         ...
         body ...)]))

(define/immutable-parameter (fib n [verbose? #f])
  ;   (set! n 12345) ;=> syntax error
  (when verbose?
    (printf "(fib ~a)\n" n))
  (cond
    [(<= n 1) n]
    [else
     (+ (fib (- n 1) verbose?)
        (fib (- n 2) verbose?))]))

Licence

I license the code in this issue under the same MIT License that the Racket language uses and the texts under the Creative Commons Attribution 4.0 International License

@Metaxal
Copy link

Metaxal commented Sep 17, 2021

That's nice!

Is there a particular reason why :id is not included in the syntax class directly? That would avoid the need for (and _:id ...).

@shhyou
Copy link
Author

shhyou commented Sep 17, 2021

Some macros may let-bind subforms to first evaluate them for later use. Therefore the subforms can be any expressions. I couldn't find a way to pass syntax classes around or compose them, so the _:id specification is left out of the syntax class.

bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Sep 21, 2021
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 27, 2021
bennn added a commit to syntax-objects/syntax-parse-example that referenced this issue Oct 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants