I ran into this issue recently. The right answer for me was to write a
trampolining macro. define-values is treated specially by the macro
expander and thus can't be handled easily in a local expand. The pattern I
ended up using (adapted from an old mailing list post by Ryan and a
discussion with Matthew and Robby) is the following:

1) expand in whatever the current syntax context is and add define-values
to the stop list (to prevent attempting to expand it outside of a
definition context)

(local-expand my-stx (syntax-local-context) (list #'define-values))

2) Use syntax-case on the resulting syntax to find any occurrences of
define-values, then replace the body of each define-values with a new call
to your macro:

(define-syntax (my-syntax stx)
  (syntax-case stx ()
    [(my-syntax expr)
     (syntax-case (local-expand stx (syntax-local-context) (list
#'define-values)) ()
       [(define-values (id) expr) (define-values (id) (my-syntax expr))]
       [other other])]))

Note this code might not work verbatim... I abstracted it from my example
as I pasted it in...

This pattern seems fairly robust for getting your macro to run on all of
the expanded code in a local context. The downside is that your macro will
need to work correctly in this "staged" fashion where things run as the
expander gets to them.

Hope this helps.

Cheers,
Scott

On Tue, Jun 23, 2015 at 2:23 PM, Sam Tobin-Hochstadt <sa...@cs.indiana.edu>
wrote:

> Based on the documentation, I thought that the right answer would be
> `(list (gensym))` but that didn't work -- hopefully someone else
> knows.
>
> Sam
>
> On Tue, Jun 23, 2015 at 4:43 PM, Thomas Dickerson
> <thomas_dicker...@brown.edu> wrote:
> > I thought that might be the case, but the documentation is pretty dense
> (and
> > self-referential), so it's not clear what the correct value for that
> > argument is.
> >
> > Thomas Dickerson
> >
> > Brown University
> > Department of Computer Science
> > 115 Waterman St, 4th Floor
> > Providence, RI 02912
> >
> > 802-458-0637
> >
> > On Tue, Jun 23, 2015 at 3:57 PM, Sam Tobin-Hochstadt <
> sa...@cs.indiana.edu>
> > wrote:
> >>
> >> To fix your last issue, you probably want to provide a different value
> as
> >> the `context-v` argument (the 2nd one) to `local-expand`.
> >>
> >> Sam
> >>
> >> On Tue, Jun 23, 2015 at 3:46 PM Thomas Dickerson
> >> <thomas_dicker...@brown.edu> wrote:
> >>>
> >>> Okay - for posterity's sake, here's an updated version of Alex's code
> >>> that supports nested Loop/Accum, and doesn't leave any "syntactic
> residue"
> >>> after expansion. This is now what I set out to accomplish with my
> original
> >>> set of questions:
> >>>>
> >>>> #lang racket/base
> >>>>
> >>>> (require racket/stxparam
> >>>>          (for-syntax racket/base
> >>>>
> >>>>                      racket/set
> >>>>                      racket/syntax
> >>>>                      syntax/parse
> >>>>                      syntax/stx
> >>>>                      syntax/context))
> >>>>
> >>>> (define-syntax Loop
> >>>>   (lambda (stx)
> >>>>     (syntax-parse stx
> >>>>       [(Loop accum-id:id up-to-expr:expr body:expr ...+)
> >>>>        (let* ([defs (syntax-local-make-definition-context)]
> >>>>               [ctx (generate-expand-context)])
> >>>>          (syntax-local-bind-syntaxes
> >>>>           (list #'accum-id)
> >>>>           (local-transformer-expand
> >>>>            #'(let ([vars (mutable-set)])
> >>>>                (lambda ([stx #f])
> >>>>                  (if (syntax? stx)
> >>>>                      (syntax-parse stx
> >>>>                        [(accum-id x:id dx:expr)
> >>>>                         (set-add! vars (syntax-local-introduce #'x))
> >>>>
> >>>>
> >>>>                         #'(set! x (+ x dx))])
> >>>>
> >>>>                      vars))) 'expression null) defs)
> >>>>          (internal-definition-context-seal defs)
> >>>>          (with-syntax* ([(body ...)
> >>>>                          (stx-map
> >>>>                           (lambda (body)
> >>>>                             (with-syntax ([body body])
> >>>>                               (local-expand #'body ctx null defs)))
> >>>>                           #'(body ...))]
> >>>>                         [(init ...)
> >>>>                          (map
> >>>>                           (lambda (var)
> >>>>                             (with-syntax ([var (syntax-local-introduce
> >>>> var)]) #'(set! var 0)))
> >>>>                           (set->list
> >>>>                            ((syntax-local-value
> >>>>                              (internal-definition-context-apply defs
> >>>> #'accum-id)
> >>>>                              #f defs))))])
> >>>>            #'(let ([up-to up-to-expr])
> >>>>
> >>>>
> >>>>                (letrec
> >>>>                    ([loop
> >>>>                      (lambda (n)
> >>>>                        body ...
> >>>>
> >>>>                        (let ([n (add1 n)])
> >>>>                          (if (< n up-to)
> >>>>                              (loop n)
> >>>>                              (void))))])
> >>>>                  init ...
> >>>>                  (loop 0)))))]
> >>>>
> >>>>
> >>>>       [(Loop up-to-expr:expr body:expr ...+)
> >>>>
> >>>>        (with-syntax
> >>>>            ([Accum (datum->syntax stx 'Accum)])
> >>>>          #'(Loop Accum up-to-expr body ...))])))
> >>>>
> >>>> (let ([x (void)] [y (void)] [z (void)])
> >>>>   (Loop 2
> >>>>         (let ([dx 1] [dy 2] [dz 3])
> >>>>           (Accum x dx)
> >>>>           (Loop AccumInner 2
> >>>>
> >>>>
> >>>>                 (printf "x = ~v, y = ~v, z = ~v\n" x y z)
> >>>>
> >>>>                 (Accum y dy)
> >>>>                 (AccumInner z dz)))))
> >>>
> >>>
> >>> One last question - the change to using local-expand meant that the
> loop
> >>> body was no longer happy accepting define-values forms and raised "Not
> in a
> >>> definition context" errors. I can use let to accomplish the same thing
> >>> without any problems, but if someone could explain what changes would
> need
> >>> to be made to the local-expand call for that to work, I'd be grateful
> (for
> >>> my own understanding).
> >>>
> >>>
> >>> Thomas Dickerson
> >>>
> >>> Brown University
> >>> Department of Computer Science
> >>> 115 Waterman St, 4th Floor
> >>> Providence, RI 02912
> >>>
> >>> 802-458-0637
> >>>
> >>> On Tue, Jun 23, 2015 at 11:17 AM, Thomas Dickerson
> >>> <thomas_dicker...@brown.edu> wrote:
> >>>>
> >>>> On Tuesday, June 23, 2015 at 8:30:32 AM UTC-4, Matthew Flatt wrote:
> >>>> > Providing #f as the third argument to `local-expand` means that
> >>>> >
> >>>> >  (+ i j)
> >>>> >
> >>>> > is expanded only as far as exposing the primitive function-call
> form,
> >>>> > also known as `#%plain-app`:
> >>>> >
> >>>> >  (#%plain-app + i j)
> >>>> >
> >>>> > When `i` is encountered later, there's no binding in the
> compile-time
> >>>> > environment, because the internal-definition context has been
> >>>> > discarded as the `my-def-stx` macro returned.
> >>>> >
> >>>> > Probably you want to replace the `#f` with `null` (which means
> "expand
> >>>> > without stopping").
> >>>> Got it - this makes sense, thanks.
> >>>>
> >>>> > Another possibility is to expand to a
> >>>> > `letrec-syntaxes+values` form to bind `i` and `j`.
> >>>>
> >>>> This is actually what I'm trying to avoid - see the earlier note on
> why
> >>>> I need a solution that won't leave behind empty (let-values () ...)
> forms
> >>>> (or any other Racket-specific forms) in the output syntax.
> >>>>
> >>>> > At Tue, 23 Jun 2015 02:13:19 -0400, Thomas Dickerson wrote:
> >>>> > > I seem to be missing some key ingredient here. The following
> really
> >>>> > > simply
> >>>> > > test-case, using let-syntaxes, works as expected:
> >>>> > >
> >>>> > > > (define-syntax my-def-stx
> >>>> > > >   (lambda (stx)
> >>>> > > >     (syntax-case stx (my-def-stx)
> >>>> > > >       [(my-def-stx (id ...) rhs expr)
> >>>> > > >        #'(let-syntaxes ([(id ...) rhs]) expr)])))
> >>>> > > >
> >>>> > > > (my-def-stx
> >>>> > > >  (i j) (values (lambda (stx) #'3) (lambda (stx) #'4))
> >>>> > > >  (+ i j))
> >>>> > > >
> >>>> > > Trying the same thing, but with this:
> >>>> > >
> >>>> > > > (require (for-syntax syntax/context))
> >>>> > > > (define-syntax my-def-stx
> >>>> > > >   (lambda (stx)
> >>>> > > >     (syntax-case stx (my-def-stx)
> >>>> > > >       [(my-def-stx (id ...) rhs expr)
> >>>> > > >        (let* ([intdef (syntax-local-make-definition-context)]
> >>>> > > >              [ctx (generate-expand-context)])
> >>>> > > >            (syntax-local-bind-syntaxes (syntax->list #'(id ...))
> >>>> > > > (local-transformer-expand #'rhs 'expression null) intdef)
> >>>> > > >            (internal-definition-context-seal intdef)
> >>>> > > >            (local-expand #'expr ctx #f intdef))])))
> >>>> > > >
> >>>> > > > (my-def-stx
> >>>> > > >  (i j) (values (lambda (stx) #'3) (lambda (stx) #'4))
> >>>> > > >  (+ i j))
> >>>> > > >
> >>>> > > instead, gives me "i: undefined; cannot reference an identifier
> >>>> > > before its
> >>>> > > definition".
> >>>> > >
> >>>> > > Thomas Dickerson
> >>>> > >
> >>>> > > Brown University
> >>>> > > Department of Computer Science
> >>>> > > 115 Waterman St, 4th Floor
> >>>> > > Providence, RI 02912
> >>>> > >
> >>>> > > 802-458-0637
> >>>> > >
> >>>> > > On Mon, Jun 22, 2015 at 11:39 PM, Matthew Flatt <
> mfl...@cs.utah.edu>
> >>>> > > wrote:
> >>>> > >
> >>>> > > > Probably you don't want to work with namespaces, which are
> >>>> > > > intended more
> >>>> > > > for run-time reflection.
> >>>> > > >
> >>>> > > > For an example of turning internal definitions into
> >>>> > > > 'letrec-syntaxes+values', try the implementation of
> >>>> > > > 'racket/block'.
> >>>> > > >
> >>>> > > > > On Jun 23, 2015, at 10:06 AM, Thomas Dickerson <
> >>>> > > > thomas_dicker...@brown.edu> wrote:
> >>>> > > > >
> >>>> > > > > Update / addendum: I was also able to fairly trivially extend
> >>>> > > > > Alex's
> >>>> > > > code using let-syntax to have the more hygienic behavior for
> where
> >>>> > > > Accum
> >>>> > > > is/is-not visible, but let-syntax ends up leaving an additional
> >>>> > > > layer of
> >>>> > > > empty (let-values() ...) behind.
> >>>> > > > >
> >>>> > > > > The documentation for let-syntax makes the following claim -
> >>>> > > > > "The
> >>>> > > > evaluation of each trans-expr is parameterized to set
> >>>> > > > current-namespace to
> >>>> > > > a namespace that shares bindings and variables with the
> namespace
> >>>> > > > being
> >>>> > > > used to expand the let-syntax form, except that its base phase
> is
> >>>> > > > one
> >>>> > > > greater." which seems very much related to what I'd like to
> >>>> > > > accomplish (and
> >>>> > > > Shriram's suggestion that I mentioned in the original post);
> >>>> > > > however, I see
> >>>> > > > no evidence of this actually taking place in the Racket
> >>>> > > > source-code, it
> >>>> > > > just appears to desugar, with very few bells and whistles, to
> >>>> > > > letrec-syntaxes+values which evidently is implemented in the C
> >>>> > > > code.
> >>>> > > > Digging into syntax-parameterize shows that, unsurprisingly,
> this
> >>>> > > > is also
> >>>> > > > how syntax-parameterize gets desugared, with a few more bells
> and
> >>>> > > > whistles.
> >>>> > > > >
> >>>> > > > > So I guess at this point the remainder of my question boils
> down
> >>>> > > > > to
> >>>> > > > this: can someone offer any insight into the process of turning
> >>>> > > > macros
> >>>> > > > which are introduced by let-syntax or equivalent into macros
> which
> >>>> > > > are
> >>>> > > > introduced implicitly by an internal-definition-context argument
> >>>> > > > to
> >>>> > > > local-expand, or by explicit namespace manipulation as hinted at
> >>>> > > > in the
> >>>> > > > let-syntax documentation (but which I can't turn up anywhere).
> >>>> > > > >
> >>>> > > > >> On Monday, June 22, 2015 at 5:35:50 PM UTC-4, Thomas
> Dickerson
> >>>> > > > >> wrote:
> >>>> > > > >> Thanks for the effort that went into figuring that out! A
> >>>> > > > >> couple first
> >>>> > > > thoughts on the implementation:
> >>>> > > > >> To first order, this is exactly what I want to be able to do
> >>>> > > > >> (and it
> >>>> > > > elegantly avoids local-expand by reasoning about expansion
> order);
> >>>> > > > however,
> >>>> > > > as a general pattern, it's a bit unhygienic in that Accum is now
> >>>> > > > in the
> >>>> > > > global namespace, which precludes using it as a child macro for
> >>>> > > > other
> >>>> > > > constructs, and prevents specific instantiations of Loop from
> >>>> > > > requesting a
> >>>> > > > different name for Accum to cooperate in nested loops.
> >>>> > > > >>
> >>>> > > > >> I also noticed that (syntax-parameterize) isn't totally
> >>>> > > > >> transparent in
> >>>> > > > the expanded syntax - it leaves a bunch of empty (let-values ()
> >>>> > > > ...)
> >>>> > > > behind, which is fine for Racket, but problematic in my target
> >>>> > > > use-case of
> >>>> > > > emitting something which ultimately needs to be an entirely
> >>>> > > > different
> >>>> > > > language. This is why my experiments have all involved
> >>>> > > > local-expand - I
> >>>> > > > want to introduce expand-time names without introducing wrapping
> >>>> > > > let-syntaxes (or similar) forms around my body code.
> >>>> > > > >>
> >>>> > > > >>
> >>>> > > > >>> On Saturday, June 20, 2015 at 2:24:40 PM UTC-4, Alex Knauth
> >>>> > > > >>> wrote:
> >>>> > > > >>>> On Jun 19, 2015, at 7:44 PM, Thomas Dickerson <
> >>>> > > > thomas_dicker...@brown.edu> wrote:
> >>>> > > > >>>>
> >>>> > > > >>>> I was intending for that example to have the variables be
> >>>> > > > >>>> defined
> >>>> > > > outside the macro, but being able to create the (set! ...) forms
> >>>> > > > outside
> >>>> > > > should mean I could also hypothetically create let/define-values
> >>>> > > > forms.
> >>>> > > > This is why I originally specified being able to effect
> generation
> >>>> > > > of both
> >>>> > > > prologue + epilogue code.
> >>>> > > > >>>
> >>>> > > > >>> I figured this out for code that will be expanded after the
> >>>> > > > >>> body:
> >>>> > > > >>>
> >>>> > > > >>> #lang racket/base
> >>>> > > > >>>
> >>>> > > > >>> (require racket/stxparam
> >>>> > > > >>>         (for-syntax racket/base
> >>>> > > > >>>                     syntax/parse
> >>>> > > > >>>                     racket/set
> >>>> > > > >>>                     ))
> >>>> > > > >>>
> >>>> > > > >>> ;; (syntax-parameter-value vars) : (U #f (MutableSetof
> >>>> > > > >>> Identifier))
> >>>> > > > >>> (define-syntax-parameter vars #f)
> >>>> > > > >>>
> >>>> > > > >>> (define-syntax Accum
> >>>> > > > >>>  (lambda (stx)
> >>>> > > > >>>    (syntax-parse stx
> >>>> > > > >>>      [(Accum x:id dx:expr)
> >>>> > > > >>>       (define vs (syntax-parameter-value #'vars))
> >>>> > > > >>>       (unless (set-mutable? vs)
> >>>> > > > >>>         (raise-syntax-error #f "cannot be used outside Loop"
> >>>> > > > >>> stx))
> >>>> > > > >>>       (set-add! vs (syntax-local-introduce #'x))
> >>>> > > > >>>       #'(set! x (+ x dx))])))
> >>>> > > > >>>
> >>>> > > > >>> (define-syntax set-accums-zero!
> >>>> > > > >>>  (syntax-parser
> >>>> > > > >>>    [(set-accums-zero!)
> >>>> > > > >>>     #:with [x ...]
> >>>> > > > >>>     (map syntax-local-introduce (set->list
> >>>> > > > >>> (syntax-parameter-value
> >>>> > > > #'vars)))
> >>>> > > > >>>     #'(begin (set! x 0) ...)]))
> >>>> > > > >>>
> >>>> > > > >>> (define-syntax Loop
> >>>> > > > >>>  (syntax-parser
> >>>> > > > >>>    [(Loop up-to-expr:expr body:expr ...+)
> >>>> > > > >>>     #'(syntax-parameterize ([vars (mutable-set)])
> >>>> > > > >>>         (let ([up-to up-to-expr])
> >>>> > > > >>>           (letrec
> >>>> > > > >>>               ([loop
> >>>> > > > >>>                 (lambda (n)
> >>>> > > > >>>                   body
> >>>> > > > >>>                   ...
> >>>> > > > >>>                   (if (< n up-to)
> >>>> > > > >>>                       (loop (add1 n))
> >>>> > > > >>>                       (void)))])
> >>>> > > > >>>             (set-accums-zero!)
> >>>> > > > >>>             (loop 0))))]))
> >>>> > > > >>>
> >>>> > > > >>> (let ([x "x"] [y "y"] [z "z"])
> >>>> > > > >>>  (Loop 10
> >>>> > > > >>>        (begin
> >>>> > > > >>>          (define-values
> >>>> > > > >>>            [dx dy dz]
> >>>> > > > >>>            (values 1 2 3))
> >>>> > > > >>>          (printf "x = ~v, y = ~v, z = ~v\n" x y z)
> >>>> > > > >>>          (Accum x dx)
> >>>> > > > >>>          (Accum y dy)
> >>>> > > > >>>          (Accum z dz))))
> >>>> > > > >>>
> >>>> > > > >>> To generate a let outside of that you’d probably need to do
> >>>> > > > >>> some kind
> >>>> > > > of local-expand, but I’m not sure.
> >>>> > > > >
> >>>> > > > > --
> >>>> > > > > 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 racket-users+unsubscr...@googlegroups.com.
> >>>> > > > > For more options, visit https://groups.google.com/d/optout.
> >>>> > > >
> >>>> > >
> >>>> > > --
> >>>> > > 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 racket-users+unsubscr...@googlegroups.com.
> >>>> > > For more options, visit https://groups.google.com/d/optout.
> >>>>
> >>>
> >>> --
> >>> 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 racket-users+unsubscr...@googlegroups.com.
> >>> For more options, visit https://groups.google.com/d/optout.
> >
> >
> > --
> > 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 racket-users+unsubscr...@googlegroups.com.
> > For more options, visit https://groups.google.com/d/optout.
>
> --
> 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 racket-users+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
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 racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to