But I think it’s important that it doesn’t use gensym or something like that,
it uses syntax-marks, which means you can break these lexical scoping rules if
you want/need to by using either syntax-local-introduce or datum->syntax:
#lang racket
(require syntax/parse/define)
(define-simple-macro (with-tables stem body ...)
#:with table-author-id (syntax-local-introduce #'table-author)
(let([table-publication (string-append stem "_publication")]
[table-author-id (string-append stem "_author")]
[table-bridge-publication-author (string-append stem
"_bridge_publication_author")]
[table-unique-counters (string-append stem "_unique_counters")]
)
body ...
))
(with-tables "x" table-author) ;”x_author"
On Jan 15, 2015, at 9:23 PM, Alexander McLin <[email protected]> wrote:
> Warning I am still a Racket intermediate user but I've been studying
> syntactic extensions a lot the past several months.
>
> The problem here is macros in Racket have lexical scope just like procedures,
> they are hygienic macros. The identifiers you introduced in the with-tables
> macro only exist or refer to other bindings in the same lexical scope as
> where you originally wrote the macro.
>
> When you invoke the macro and pass in table-author, even though it is spelled
> the same as the identifier you wrote in the macro definition, they are not
> the same. When the macro expands, hygiene is implemented by renaming all
> identifiers in the macro to unique non-clashing symbols that don't conflict
> with others existing in the scope the macro is expanding in.
>
> The table-author identifier in the macro in the let form is renamed to
> something different like g6271 or something along those lines.
>
> Furthermore, you need to be careful about what you mean by evaluation. In the
> presence of macros, you have the concept of syntax phase(or compile-time or
> expand-time) evaluation versus run-time evaluation. When the macro is
> expanding, it does it thing, processing the original syntax into the new
> piece of syntax that replaces what was there previously such as (with-tables
> "x" table-author) which is then finally evaluated during run-time.
>
> (with-tables "x" table-author) will expand into something looking similar to
> the following, just to give you an idea of what macro expansion looks like:
>
> (let ((g6191 (string-append "x" "_publication"))
> (g6271 (string-append "x" "_author"))
> (g6369 (string-append "x" "_bridge_publication_author"))
> (g6445 (string-append "x" "_unique_counters")))
> table-author)
>
> Note that the original table-author identifier has been replaced by a
> different identifier that still has the same binding you originally defined.
>
> The table-author identifier you passed to the macro gets inserted in the body
> position and then the expanded code is evaluated at run-time and of course
> gives you a run-time error since table-author does not refer to anything and
> thus when it's evaluated, it is recognized as an undefined identifier.
>
> (with-tables "x" "hello") works because what you get in return is:
>
> (let ((g6191 (string-append "x" "_publication"))
> (g6271 (string-append "x" "_author"))
> (g6369 (string-append "x" "_bridge_publication_author"))
> (g6445 (string-append "x" "_unique_counters")))
> "hello")
>
> "hello" is just a self-evaluating string giving you back "hello" from within
> the let form.
>
> On Thu, Jan 15, 2015 at 12:12 AM, Thomas Lynch
> <[email protected]> wrote:
> I have a simple syntax rule:
>
> Welcome to Racket v5.2.1.
> racket@> (define-syntax-rule (with-tables stem body ...)
> (let(
> [table-publication (string-append stem "_publication")]
> [table-author (string-append stem "_author")]
> [table-bridge-publication-author (string-append stem
> "_bridge_publication_author")]
> [table-unique-counters (string-append stem "_unique_counters")]
> )
> body ...
> ))
>
> Which works fine when I don't reference the environment defined by the let:
>
> racket@>
> racket@> (with-tables "x" "hello")
> "hello"
>
>
> However when I pass it an identifier corresponding to one of the variables
> defined in the let:
>
> racket@> (with-tables "x" table-author)
> reference to undefined identifier: table-author
> stdin::1167: table-author
>
> The identifier passed in doesn't seem to be part of the local let context,
> but carried in a different context, or perhaps it was evaluated as an
> operand. I didn't expect either of those. Can someone point me at a
> description of the expected behavior, or give me a tip here on what is
> happening and why.
>
> ... in Wolfram language there is a 'Hold' operator for situations like this.
> Apparently inside the macro we have to do some evaluation to handle the work
> of the macro, is that why the operand is evaluated?
>
> Thanks in advance for explaining the evaluation/context model here.
>
> Thomas
>
> _________________________
> Racket Developers list:
> http://lists.racket-lang.org/dev
>
>
> _________________________
> Racket Developers list:
> http://lists.racket-lang.org/dev
_________________________
Racket Developers list:
http://lists.racket-lang.org/dev