[plt-scheme] Reproviding "else"

From: Ryan Culpepper (ryanc at ccs.neu.edu)
Date: Mon Mar 16 18:52:56 EDT 2009

On Mar 16, 2009, at 6:08 PM, Neil Toronto wrote:

> Ryan Culpepper wrote:
>> On Mar 16, 2009, at 3:19 PM, Neil Toronto wrote:
>>> The attached example language module "cond-lang.ss" implements a  
>>> very small language, reproviding a few bits of Scheme and  
>>> providing a new cond macro. When "cond-test.ss" uses that macro,  
>>> the compiler gives an error:
>>>   compile: unbound identifier (and no #%top syntax transformer
>>>   is bound) in: else
>>> It's not just else, though: you can't use as a literal any name  
>>> that's in scope in the macro definition. (For example, the  
>>> compiler gives the same error if else is changed to foobar and  
>>> foobar is defined at the top level.) Well, you can, but you have  
>>> to provide that name as well as the macro. If you can't safely do  
>>> that, there are uglier ways.
>>> Why are literals with syntax properties used for matching?
>> Rather, the literals are matched by *binding*, not by their  
>> symbolic name. The macro is looking for the 'else' in scope in  
>> "cond-lang.ss", which is the 'else' from the 'scheme' language. But  
>> the 'else' used in "cond-test.ss" is something else, so the macro  
>> interprets it as a 'text-expr^', and it eventually gets flagged as  
>> an unbound variable. The solution, as you remark, is to provide  
>> 'else'.
>> So, why are literals matched by binding, rather than by symbolic  
>> name?
>> It's a matter of syntax design. Your macro analyzes its clauses and  
>> tries to distinguish between an else-clause and an ordinary test- 
>> clause. In order for the macro to distinguish the two kinds of  
>> clauses, they should not overlap. That is, there shouldn't be any  
>> valid test-clause that is also an else-clause. They have the same  
>> shape, so the thing that really distinguishes them is the first  
>> part: There shouldn't be any valid test expression that is also the  
>> else keyword.
>> If keywords were recognized by just their spelling, then there  
>> would be an overlap between the else keyword and a local variable  
>> called 'else' in the "cond-test.ss" module. Instead, the 'else'  
>> keyword is defined as a macro that always raises an error when used  
>> as an expression. That makes 'else' effectively not a valid  
>> expression, so there's no overlap.
>> The alternative would be to say that a test-expression is almost  
>> like an ordinary expression, except that it can't be a variable  
>> named 'else'. This might seem innocuous---who uses 'else' as a  
>> variable name? But it's a hidden danger and, at the same time,  
>> unnecessary clutter in the meaning of 'new:cond'. Better to  
>> *reserve* the word right off the bat and learn of conflicts as  
>> early as possible.
>> There are other advantages that come from treating keywords as  
>> bindings, including the ability to rename them when you import them  
>> into a module, the ability to keep some keywords internal (just  
>> don't export them), and the ability to get nice documentation links  
>> for keywords via Scribble :)
> Ah, okay. Dark corner illuminated. Thanks!
> Two questions. First, doesn't treating literals as bindings (when  
> they exist) mean that macros aren't isolated by default from  
> additions to required libraries? A third-party library can break my  
> macro by doing nothing more than introducing a new name that happens  
> to match one of my macro's literals.

Yes. In general, if one library adds an export, then any of its  
clients might break, if they were already defining or importing that  
name. The module that requires both your library and the other library  
would no longer compile, because it's getting the same identifier from  
two sources. You would have to exclude or rename one of the imports  
(see the rename-in require form).

> Second, doing a "(define <literal> (void))" and providing it for  
> every literal would let me keep proper syntactic abstraction. Should  
> I do something like that?

If you do that, it's still an expression. Here is the PLT idiom:

(define-syntax <literal>
   (lambda (stx)
     (raise-syntax-error #f "keyword used as an expression" stx)))

The particular error message is usually customized to the keyword or  
the macro that uses it.


Posted on the users mailing list.