[plt-scheme] Reproviding "else"

From: Ryan Culpepper (ryanc at ccs.neu.edu)
Date: Mon Mar 16 16:42:08 EDT 2009

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 :)

Ryan


> #lang scheme
>
> (define-syntax new:cond
>  (syntax-rules (else)
>    [(_)  (void)]
>    [(_ [else else-expr])  else-expr]
>    [(_ [test-expr^ then-expr^] [test-expr then-expr] ...)
>     (if test-expr^
>         then-expr^
>         (new:cond [test-expr then-expr] ...))]))
>
> (provide
> #%module-begin #%datum #%app if define
> (rename-out [new:cond cond])
> < quote)
> #lang s-exp "cond-lang.ss"
>
> (define (classify-age n)
>  (cond [(< n 0) 'twinkle-in-daddys-eye]
>        [(< n 10) 'precocious]
>        [(< n 20) 'cheeky]
>        [(< n 30) 'spunky]
>        [(< n 50) 'not-dead-yet]
>        [(< n 120) 'cobol-programming]
>        [else  'holy-fate-worse-than-death-batman!]))
>
> (classify-age 121)
> _________________________________________________
>  For list-related administrative tasks:
>  http://list.cs.brown.edu/mailman/listinfo/plt-scheme



Posted on the users mailing list.