[racket] ok what's wrong there 'syntax-rule' evaluating its operand (??)

From: Thomas Lynch (thomas.lynch at reasoningtechnology.com)
Date: Wed Jan 14 23:46:22 EST 2015

Matthias,

I appreciate your patience.  I did see the 'pattern better than direct
access' and other style discussion on a tutorial on the net, and I do get
that.  The first macro presented in the original post was to show the
desired behavior and to show the code runs without errors.  This stands in
contrast to the define-syntax-rule example, which throws an error - note
the subject of my post.

You haven't commented on the error or why the syntax rule evaluates the
operand.  If "macros do not evaluate anything", which was my expectation
also, and the expansion occurs in hygienically at the scope of the let at
run time, which was also my expectation,  then where is the 'undefined
identifier' error coming from?

You can see the error thrown by running the define-syntax-rule example, as
shown in the original post along with its output.

Thomas



On Thu, Jan 15, 2015 at 12:07 PM, Matthias Felleisen <matthias at ccs.neu.edu>
wrote:

>
> [cc user@ again]
>
> There is no bug in syntax-rule/s.
>
> Macros do not evaluate anything. They rewrite code. Code refers to
> identifiers. This relation must remain apparent.
>
> That's it. -- Matthias
>
>
>
>
>
>
> On Jan 14, 2015, at 9:57 PM, Thomas Lynch wrote:
>
> As for the demonstrative example at the top of the original post, I now
> understand you are speaking of clarity and style rather than a functional
> problem with it. I enjoyed that discussion on clarity, intention, and
> aesthetics.
>
> As for the question of why the 'body' argument is being evaluated as an
> operand rather as part of the expansion, and thus resulting in an undefined
> identifier error, am I correct to read your answer above, that this is
> indeed a bug?  "macro-site variables are bound by macro-contexts" but
> clearly that is not happening - or the error wouldn't be issued.
>
> IMHO the define-syntax-rule would be the clearest implementation for the
> reader for the reasons you give.  Too bad there is a bug in it.  (The
> operand should not be evaluated except as part of the macro expansion, as
> you note.  I am reading you on this correctly?  I have a hard time
> understanding why an operand to a macro would be evaluated before it is
> included in the expansion, unless it was a limitation of the interpreter,
> but if it is a bug, then good as it can be fixed.)
>
> On Thu, Jan 15, 2015 at 9:53 AM, Matthias Felleisen <matthias at ccs.neu.edu>
> wrote:
>
>>
>> On Jan 14, 2015, at 8:14 PM, Thomas Lynch wrote:
>>
>> Matthias thank you for fielding my question, though two things are not
>> clear.  I hope you or someone might clarify.
>>
>> Firstly,  is there a reason to prefer the macro you present over the
>> first one given in the original post.  I believe they both work.  Neither
>> uses the more elegant syntax-rule.
>>
>>
>>
>> Code that works is fine. Code that clearly explains its intention is
>> better. Since syntactic extensions are (basically) extensions to the
>> grammar, their specification ought to bring across (a) what kind of new
>> expressions programmers may write down and (b) what these new expressions
>> mean. I think separating these cleanly is also important.
>>
>> With syntax-case, which isn't all that different from syntax-rules, you
>> get (a) easily:
>>
>>   (with-tables stem body ...)
>>
>> Compare this to your three or four lines that stx apart. When your
>> extension is even more complicated, you definitely want this pattern
>> matching notation because it is close to the way people write grammars and
>> because you can automatically check certain properties (see syntax-parse).
>>
>> For (b), constructing a piece of syntax manually with backquote, comma,
>> splice is again much more complicated than writing down a rewrite rule. In
>> particular, you get a really good handle at the manipulation of scope,
>> which is one of the primary functions of syntactic extensions.
>>
>> Racket like Scheme like Lisp is about being able abstract boiler plate.
>> Syntax-case abstracts your boiler plate for (a) and (b).
>>
>> Ideally, I would like to separate (a) from (b) so that programmers can
>> specify (a) at the module boundary, just like contracts for functions.
>> That's what I started Ryan on and we ended up syntax-parse. It' s a
>> fantastic first step but there is work left to do. Probably another
>> dissertation.
>>
>>
>> Secondly, and this is the question I'm really getting at,  is there a
>> reason that the operands given to syntax-rules must have identifiers within
>> lexical scope at the point of the macro call, rather than lexical scope at
>> the point of their use within the macro?
>>
>>
>> Lexical scope is critical for program comprehension. Programmers read
>> programs a lot more often than they write them. Lexical scope guarantees
>> that when they read programs all identifiers are resolved (bound) to a
>> declaration (binding position) that can be found by reading the text -- not
>> running the program. By finding it in the text w/o running it, you have a
>> better chance of predicting what the program does when you run it.
>>
>> Macros rewrite program text. In the process, they substitute use-site
>> code into macro-definition code and macro-definition code is substituted
>> into use-site code. Each substitution may affect lexical scope when
>> performed without respect to lexical scope. Each substitution may thus bind
>> variables to binding occurrences that you can only figure out by running
>> code. Enforcing that
>>
>>   use-site variables are bound by use-context
>> macro-site variables are bound by macro-contexts
>>
>> ___by default___ is called hygienic expansion and almost always gives you
>> what you want.
>>
>> On rare occasion, an extension of Racket's grammar also needs to break
>> these default rules. syntax-case/parse allow programmers to break those
>> rules in a way that is still easy to read off from the code.
>>
>> That's why it's the right way to go about syntax extensions. -- Matthias
>>
>>
>>
>>
>>
>>
>>
>> Off hand the latter seems to be the proper behavior for a macro,  i.e.
>> perhaps this is a bug?  Can anyone here tell me why it behaves like this?
>>
>>
>> On Thu, Jan 15, 2015 at 4:12 AM, Matthias Felleisen <matthias at ccs.neu.edu
>> > wrote:
>>
>>>
>>> You want something like this:
>>>
>>> (define-syntax (with-tables stx)
>>>   (syntax-case stx ()
>>>     [(with-tables stem body ...)
>>>      (let ([table-author (datum->syntax stx 'table-author)]
>>>            ;; ... ditto for other identifiers for which you wish to
>>> break lexical scope
>>>            )
>>>        #`(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 ...))]))
>>>
>>> (with-tables "x" table-author)
>>>
>>> ;; ---
>>>
>>> To achieve this with syntax-rules would be, well, hard.
>>>
>>> ;; ---
>>>
>>> The accepted way of writing this macro is:
>>>
>>> (define-syntax (with-tables stx)
>>>   (syntax-case stx ()
>>>     [(with-tables stem (table-author
>>>                         ;; ... add other names you wish to bind in body
>>>                         )
>>>                   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 ...)]))
>>>
>>> (with-tables "x" (table-author) table-author)
>>>
>>>
>>>
>>>
>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20150115/a6305716/attachment.html>

Posted on the users mailing list.