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

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Wed Jan 14 23:07:10 EST 2015

[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/20150114/0a79ee57/attachment-0001.html>

Posted on the users mailing list.