> On Apr 26, 2016, at 4:38 PM, Nicholas Labich <[email protected]> wrote:
> 
> Hmm, no takers on this wall of text? ;)
> 
> Turns out I was looking in the wrong direction. While reading some of 
> Matthew's sets of scopes notes, I stumbled across an example with exactly the 
> tool I needed: `syntax-local-introduce'. Each of the intermediary 
> `let-syntax'-bound macros just needed its identifier introduced in-context.
> 
> A more minimal example of the working code is below. Now that this is solved, 
> is there a better way? Stacking units up was a bit too brittle in my 
> pre-macro attempts.
> 
> #lang racket
> (require (for-syntax syntax/parse racket/syntax)
>         racket/splicing)
> 
> (define-syntax-rule (save-bind name id val)
>  (define-syntax name (syntax [id val])))
> 
> (save-bind FOO foo 1)
> (splicing-let ([bar-local 2])
>  (save-bind BAR bar (+ bar-local foo)))
> (splicing-let ([baz-local 3])
>  (save-bind BAZ baz (+ baz-local bar)))
> 
> (define-syntax (dyn-bind stx)
>  (syntax-parse stx
> 
>    [(dyn-bind before ... name:id after ...)
>     #`(dyn-bind before ... #,(syntax-local-value #'name) after ...)]
> 
>    [(dyn-bind [id:id val:expr] after ...+)
>     #`(splicing-let-syntax
>           ([#,(syntax-local-introduce (datum->syntax #f (syntax->datum #'id)))
>             (make-set!-transformer
>              (λ (stx) (syntax-case stx ()
>                         [id (identifier? #'id) #'val])))])

There's a function for this in `syntax/transformer`. Instead this whole 
set!-transformer stuff, you can use `(make-variable-like-transformer #'val)`.

>         (dyn-bind after ...))]
> 
>    [(dyn-bind [id:id val:expr])
>     #`(define-syntax #,(format-id #'dyn-bind "~a" (syntax-e #'id))

Here you're using #'dyn-bind for the context, where before you used 
syntax-local-introduce. To be consistent with the previous case, using 
`#`(define-syntax #,(syntax-local-introduce (datum->syntax #f (syntax->datum 
#'id))) ...)` would probably be better here, because in cases where other 
macros expand to `dyn-bind` forms, this context would point to the code in that 
macro definition, which probably isn't what you wanted.

>         (make-set!-transformer
>          (λ (stx) (syntax-case stx ()
>                     [id (identifier? #'id) #'val]))))]))
> 
> (splicing-let ([bar-local 500]
>               [baz-local 1000])
>  (dyn-bind FOO BAR BAZ))
> 
> (equal? baz 6)

Since the lexical context of the ids should be handled uniformly in both cases, 
a possible better way to handle this would be to do this syntax-local-introduce 
stuff right after getting the syntax-local-value, like this:

(require (for-syntax syntax/transformer))

(define-syntax (dyn-bind stx)
   (syntax-parse stx
     [(dyn-bind before ... name:id after ...)
      #:with [id:id val:expr] (syntax-local-value #'name)
      #:with id*:id (syntax-local-introduce (datum->syntax #f (syntax-e #'id)))
      #`(dyn-bind before ... [id* val] after ...)]
     [(dyn-bind [id:id val:expr] after ...+)
      #`(splicing-let-syntax
            ([id
              (make-variable-like-transformer #'val)])
          (dyn-bind after ...))]
     [(dyn-bind [id:id val:expr])
      #`(define-syntax id
          (make-variable-like-transformer #'val))]))

As a general rule, I've found that whenever you're getting a syntax object from 
a point outside the macro, like from a mutable box, or a syntax-local-value 
call, it's a good idea to do whatever messing-with-the-context you want to do 
right there, not some point later. 

The syntax-local-introduce function is what racket uses on the inputs and 
outputs of macros to preserve hygiene. It's supposed to be applied on the 
boundary between the macro code and everything else. So if you're ever getting 
a syntax object through some other method, it's a good idea to consider whether 
or not to use syntax-local-introduce right there on that boundary. 

Where you used `syntax-local-value` is one of those places on the boundary, 
because in this case it's returning a syntax object that came from the outside. 
It turned out that you didn't want to use it on the whole thing, just on the 
`id` within it. There might be a slightly cleaner version of this that doesn't 
completely erase the lexical context of the `id` before introducing it, but I'm 
not sure how to do that.

Alex Knauth

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to