[racket-dev] define-require-syntax issue

From: Jay McCarthy (jay.mccarthy at gmail.com)
Date: Wed Oct 22 14:09:06 EDT 2014

If you have (require X) then the identifiers imported from X get the
lexical context of X. (Slight note: In something like (rename-in X [A
B]) then they get the context of A.)

If a macro made X, then the lexical context is equivalent to #f,
because every macro application gets a fresh lexical context. (This
part of how "hygiene" is implemented.)

So, since you macro produced an argument to require, the imports got
this empty lexical context.

If you had produced a single identifier, then you could've produced it
with (datum->syntax stx 'symbol) and that would make it so the symbol
would have stx's context, rather than the default which is basically
#f.

In the case of a file or submodule etc, when we construct a syntax
object with "syntax" (or quasisyntax or syntax/loc etc) there's no way
to specify what lexical context we want. (The "/loc" variants just
copy the source location, not the context.) Instead, we have to
construct the object and then go back and change the lexical context
using something like replace-context.

Thus, the (file abc) that you produce now has the right stuff. It is
possible that you could've just replace the context of "file" or of
"abc" and got the same effect, but I'm not sure (it depends on how
"file" is implemented.)

Jay




On Wed, Oct 22, 2014 at 2:03 PM, Dan Liebgold <dan.liebgold at gmail.com> wrote:
> So, yeah... that appears to work!
>
> I use replace-context to give the resulting require syntax output the
> context of the original argument. Here's what the change looks like, with my
> old way commented (unrelated note: path is actually relative):
>
> (define-require-syntax (gamelib stx)
>   (syntax-case stx ()
> ((_ name)
> ;;(quasisyntax/loc stx (file #,(format "../some/path/~a.dc" (syntax->datum
> #'name)))))
> (replace-context stx #`(file #,(format "../some/path/~a.dc" (syntax->datum
> #'name)))))
> ))
>
> I thought the quasisyntax/loc would be enough, but I guess replace-context's
> increased thoroughness is necessary.
>
> So, is the problem that the identifiers imported by the require usage get
> the lexical context of the 'define-require-syntax' declaration (where I use
> #' maybe)?  I'm don't follow what exactly goes wrong with the imported
> identifiers.
>
> Thanks,
> Dan
>
>
> On Wed, Oct 22, 2014 at 4:58 AM, Jay McCarthy <jay.mccarthy at gmail.com>
> wrote:
>>
>> #lang racket/base
>>
>> ;; This module has a binding and an effect, so we can see that it was
>> ;; required even when we can't get to it.
>> (module example racket/base
>>   (define x 1)
>>   (printf "I'm running here\n")
>>   (provide x))
>>
>> ;; If you comment this in, you'll see the "normal" way to require it.
>>
>> #;
>> (let ()
>>   (local-require (prefix-in no-macro: (submod "." example)))
>>   (printf "NM x is ~a\n" no-macro:x)
>>   (void))
>>
>> ;; Here is the "obvious" macro of tihs form
>> (require racket/require-syntax)
>> (define-require-syntax macro1
>>   (syntax-rules ()
>>     [(_) (submod "." example)]))
>>
>> ;; If you comment this in, you'll see that the effect is run, meaning
>> ;; that it really does require the right thing. Also notice that since
>> ;; I'm using submodules, the problem ISN'T that `example1` is some how
>> ;; no the right binding for the module. In your example of an absolute
>> ;; path, it's even more clear that the path isn't wrong.
>>
>> #;
>> (let ()
>>   (local-require (prefix-in macro1: (macro1)))
>>   ;; If you comment this in, you'll see that it is unbound.
>>   #;
>>   (printf "M1 x is ~a\n" macro1:x)
>>   (void))
>>
>> ;; Here is a more complicated version of the above macro. There's
>> ;; really only one meaningful difference and that's that we explicitly
>> ;; give the require syntax output the context of the CALL to
>> ;; macro2. If macro2 had an argument, it may make more sense to use
>> ;; that lexical context, because that argument probably came from the
>> ;; ultimate user of this require syntax (in case macro2 is used by
>> ;; another macro2)
>> (require (for-syntax racket/base
>>                      syntax/strip-context))
>> (define-require-syntax macro2
>>   (λ (stx)
>>     (syntax-case stx ()
>>       [(_)
>>        (replace-context stx (syntax (submod "." example)))])))
>>
>> ;; You may want to comment this out while looking at the other ones so
>> ;; you can be sure that this isn't the reason something is working.
>>
>> (let ()
>>   (local-require (prefix-in macro2: (macro2)))
>>   (printf "M2 x is ~a\n" macro2:x)
>>   (void))
>>
>> On Tue, Oct 21, 2014 at 9:26 PM, Dan Liebgold <dan.liebgold at gmail.com>
>> wrote:
>> > If I do a (require (file <some absolute path>)) in a module, the
>> > provided
>> > stuff gets imported properly.
>> >
>> > If I do a special require form that uses define-require-syntax to
>> > generate
>> > an identical (file <...>) the specified module gets evaluated -- but
>> > (seemingly) nothing gets imported.
>> >
>> > Is there something special the define-require-syntax transformer needs
>> > to do
>> > besides generate a syntax object?
>> >
>> > samth mentioned on irc that it is probably a hygiene issue... something
>> > about generating the right marks on the (file ...) form.
>> >
>> > --
>> > Dan Liebgold    [dan.liebgold at gmail.com]
>> >
>> > _________________________
>> >   Racket Developers list:
>> >   http://lists.racket-lang.org/dev
>> >
>>
>>
>>
>> --
>> Jay McCarthy
>> http://jeapostrophe.github.io
>>
>>            "Wherefore, be not weary in well-doing,
>>       for ye are laying the foundation of a great work.
>> And out of small things proceedeth that which is great."
>>                           - D&C 64:33
>
>
>
>
> --
> Dan Liebgold    [dan.liebgold at gmail.com]



-- 
Jay McCarthy
http://jeapostrophe.github.io

           "Wherefore, be not weary in well-doing,
      for ye are laying the foundation of a great work.
And out of small things proceedeth that which is great."
                          - D&C 64:33


Posted on the dev mailing list.