[racket-dev] define-require-syntax issue
A tiny note for Google... the source location information isn't part
of hygiene, it's like an orthogonal axis.
When a form like (syntax ....) is used, it creates a new piece of
syntax where the origin is that particular file/line. When you use
syntax/loc, you create a new syntax object but you copy over the
source location from the argument. You don't need to do this for
syntax template identifiers (like name in Dan's example) but you do
need to do it for syntax lists that you create.
It is good form to always use syntax/loc and quasisyntax/loc when
writing macros so that errors are always at the abstraction level of
the client code rather the implementation. This is a similar principle
to using contracts.
Jay
On Wed, Oct 22, 2014 at 2:55 PM, Dan Liebgold <dan.liebgold at gmail.com> wrote:
> That makes sense.
>
> It turns out I need replace-context *and* quasisyntax/loc (and back to
> absolute paths):
>
> (define-require-syntax (gamelib stx)
> (syntax-case stx ()
> ((_ name)
> (replace-context stx (quasisyntax/loc stx (file #,(format
> "~a/some/path/~a.dc" (current-directory) (syntax->datum #'name))))))
> ))
>
> Without the ..syntax/loc form if I name the wrong file at my usage point the
> error location is the define-require-syntax declaration. Adding the /loc
> gets the correct usage location. Hrm. Hygiene is deep magic.
>
> Thanks,
> Dan
>
>
> On Wed, Oct 22, 2014 at 11:09 AM, Jay McCarthy <jay.mccarthy at gmail.com>
> wrote:
>>
>> 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
>
>
>
>
> --
> 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