I see that the `declare` macro is using `syntax-local-get-shadower` as
a way of storing information in the compile-time environment. The
`syntax-local-get-shadower` function is still the only primitive to
provide that functionality. It's an awkward primitive, and I considered
replacing it with a primitive that is more streamlined to that task,
but it seems more likely that we'll eventually provide a nicer API as a
library built in terms of `syntax-local-get-shadower`.
So, for this macro, it makes sense to continue with
`syntax-local-get-shadower` for now.
The macro currently fails in the new expander for two reasons:
* There's a bug in the new expander that makes `syntax-local-context`
produce an inconsistent value. That's the source of of the
duplicate-definition error.
I've pushed a repair to the "scope" branch, and I'll update the
snapshot later today.
* The strategy of having a fresh mark on `old-vars` (by not using
`syntax-local-introduce`) doesn't work anymore. An extra scope on
`old-vars` doesn't prevent it from being captured by the new `vars`
binding.
I think the solution is to use `local-expand` before generating the
`vars` definition, so that `local-expand` sees the old `vars`
binding. Or, if it's important to delay the lookup that
`local-expand` performs until `vars` is called, then introduce an
indirection through a fresh name/scope for each context and use
`vars` to record that name/scope.
At Mon, 13 Apr 2015 16:09:43 +0200, Jens Axel Søgaard wrote:
> 2015-04-13 14:40 GMT+02:00 Matthew Flatt <
mfl...@cs.utah.edu>:
> > The new macro expander is ready for you to try:
> ...
>
> > * A macro that uses `syntax-local-get-shadower` like will have to be
> > revised, and it should be revised to avoid that function. A macro
> > that uses `syntax-local-make-delta-introducer` definitely will have
> > to be revised, because that function is no longer supported. Most
> > macros don't use such obscure functions.
>
> The following example shows two macros declare and vars that work
> together.
> The declare macro is used to declare identifiers (have some property) and
> the macro vars shows how a macro can access the declared identifiers that
> are in scope. Here vars simply expands to a list of the declared
> identifiers.
>
> Example:
>
> (declare a) ; module level declaration
> (display (vars))
> (let ()
> (display (vars)) ; (1)
> (declare x) ; here (1) and (2) are to display the same
> variables (same scope)
> (display (vars)) ; (2)
> (let ()
> (display (vars))
> (declare s)
> (declare t)
> (display (vars)))
> (declare y)
> (display (vars))) ; check that s and t are no longer in scope
> (declare b)
>
> ; displays: (b a)(y x b a)(y x b a)(t s y x b a)(t s y x b a)(y x b a)
>
> My solution in the old macro system used syntax-local-get-shadower
> together with syntax-local-introduce to "refresh" the vars identifier
> in order to redefine its meaning in each new scope.
>
> In what direction should I look to fix this example?
>
> [The error message with the new macro expander is:
> internal definition: duplicate binding name in: vars]
>
>
> #lang racket
> (require (for-syntax syntax/parse racket/format))
>
> (begin-for-syntax
> (define module-level-variables '())
> ; each internal definition contexts is given an index
> (define intdefs (make-hasheq))
> ; each index is associated to a list of declared names
> (define local-variables (make-hash))
>
> (define (new-intdef? intdef) (hash-has-key? intdefs intdef))
> (define (index-of-intdef intdef) (hash-ref! intdefs intdef (hash-count
> intdefs)))
> (define (add-local-var! index var)
> (hash-update! local-variables index (λ (old-vars) (cons var old-vars))
> '())))
>
> (define-syntax (vars stx)
> (with-syntax ([vs module-level-variables])
> #''vs))
>
> (begin-for-syntax
> (define refresh-identifier (compose syntax-local-introduce
> syntax-local-get-shadower)))
>
> (define-syntax (declare stx)
> (syntax-parse stx
> [(_ id)
> (define var (syntax->datum #'id))
> (define ctx (syntax-local-context))
> (cond
> [(eq? ctx 'module)
> (set! module-level-variables (cons var module-level-variables))
> #'(begin)]
> [(list? ctx) ; internal definition context
> (define old-scope? (new-intdef? ctx))
> (define index (index-of-intdef ctx))
> (add-local-var! index var)
> (cond [old-scope? #'(begin)]
> [else (with-syntax ([vars (refresh-identifier
> #'vars)]
> [old-vars
> (syntax-local-get-shadower #'vars)]
> [index index]
> [.... #'(... ...)])
> #'(define-syntax (vars st)
> (define locals (hash-ref local-variables
> index))
> (define others (local-expand #'(old-vars)
> (syntax-local-context) #f))
> ; others = (quote (var ...)) so we skip the
> quote here:
> (with-syntax ([(local ....) locals]
> [(_ (other ....)) others])
> #`'(local .... other ....))))])]
> [else (error 'declare
> (~a "declarations are only allowed at the module level "
> "and in internal definition contexts"))])]))
>
> (declare a)
> (display (vars))
> (let ()
> (display (vars))
> (declare x)
> (display (vars))
> (let ()
> (display (vars))
> (declare s)
> (declare t)
> (display (vars)))
> (declare y)
> (display (vars)))
> (declare b)
>
> ; displays: (b a)(y x b a)(y x b a)(t s y x b a)(t s y x b a)(y x b a)