free-identifier=? bug or by design?

Bug #260143 reported by leppie on 2008-08-21

This bug report was converted into a question: question #42902: Exporting auxiliary syntax keywords.

2
Affects Status Importance Assigned to Milestone
Ikarus Scheme
Undecided
Unassigned

Bug Description

Hi

I am not sure what is the correct behaviour with the following snippet/execution:

(library (foo)
  (export mycond3)
  (import (rnrs))
  (define-syntax mycond3
    (syntax-rules (else3) [(_ (else3 e)) e])))

(import (foo))

(mycond3 (else3 #t)) => #t
(define else3)
(mycond3 (else3 #t)) => syntax violation

If you do the same for 'else' there is no syntax violation, as 'else' is exported as auxiliary syntax. I however see no way for a user to export his own auxiliary syntax, and hence the syntax violation.

So, if not a bug, how can one deal with this?

Cheers

leppie

On Aug 21, 2008, at 10:22 AM, leppie wrote:

> I am not sure what is the correct behaviour with the following
> snippet/execution:
>
> (library (foo)
> (export mycond3)
> (import (rnrs))
> (define-syntax mycond3
> (syntax-rules (else3) [(_ (else3 e)) e])))

mycond3 matches on something of the form (_ (id _)) where
id is free-identifier=? to else3 in (foo). Since else3 is
unbound in (foo), it would match any unbound identifier
whose name is else3.

> (import (foo))
>
> (mycond3 (else3 #t)) => #t

Right. At the top level, else3 was unbound so it matches.

> (define else3)
> (mycond3 (else3 #t)) => syntax violation

Now it's bound, so, else3 at the top level has a different
binding from else3 in (foo), so, they two else3s are not
free-identifier=?.

> If you do the same for 'else' there is no syntax violation, as
> 'else' is exported as auxiliary syntax.

If you redefine else, you should get a syntax violation.

 > (library (foo)
     (export mycond3)
     (import (rnrs))
     (define-syntax mycond3
       (syntax-rules (else) [(_ (else e)) e])))
 > (import (foo))
 > (mycond3 (else 12))
12
 > (define else 17)
 > (mycond3 (else 12))
Unhandled exception
  Condition components:
    1. &message: "invalid syntax"
    2. &syntax:
        form: (mycond3 (else 12))
        subform: #f
    3. &trace: #<syntax (mycond3 (else 12))>

> I however see no way for a user to export
> his own auxiliary syntax, and hence the syntax violation.

Auxiliary keywords can be defined as

(define-syntax else3
   (lambda (x)
     (syntax-violation #f "incorrect usage of auxiliary keyword" x))

leppie (leppie) wrote :

Hi Aziz

Thanks for the explanation.

This leads me to believe one should 'never' use auxiliary keywords. For example, in my case, a single macro might have several (about 10) auxiliary keywords (some of whom are 'let' and '='). The way I see it, I have 2 options:

1. Leave as is, and pray no one redefines an auxiliary keyword (does the original described behaviour only occur when redefined after a library has been imported?).
2. Export all auxiliary keywords, but then I have the problem that it will clash with existing keywords, eg 'let' & '=', when imported.

Ideally I would like it to be like (1), but without the ability to redefine the keywords. Maybe there is a way to achieve this, by creating the keywords via datum->syntax? Eg:

(with-syntax ((let (datum->syntax #'k 'let))) ...

Thanks

leppie

leppie (leppie) wrote :

I wrote:

> does the original described behaviour only occur when redefined after a library has been imported?

It appears either way.

> (define select)
> (import (ironscheme linq))
> (from x in '(1 2 3) select x)
&message: "invalid syntax"
&syntax: (select x)

Any ideas how to 'solve' this issue would be greatly appreciated.

Thanks

leppie

leppie (leppie) wrote :

I wrote:

> Maybe there is a way to achieve this, by creating the keywords via datum->syntax?

Nope, tried it, does not work.

If I do something like the following I get the desired results, but that seems rather long in the tooth!

[(select s into k rest ...) ; select removed 'free-identifier=?' matchers
  (eq? (syntax->datum #'select) 'select)
  clauses ...

I cant help but feel this seems a bit 'noobish', hehe, is there a better way?

Cheers

leppie

leppie (leppie) wrote :

Hi again

I wrote:

> but that seems rather long in the tooth!

This got me thinking, one could probably write a nice macro for this.

Maybe worthy of the macro challenge? :-)

Cheers

leppie

On Aug 22, 2008, at 8:55 AM, leppie wrote:

> This leads me to believe one should 'never' use auxiliary keywords.

They are a little confusing and inconvenient, I agree.

> For
> example, in my case, a single macro might have several (about 10)
> auxiliary keywords (some of whom are 'let' and '='). The way I see
> it, I
> have 2 options:

> 1. Leave as is, and pray no one redefines an auxiliary keyword

This has been the case in R5RS where if you define else, cond and
case stop working properly. In R6RS (libraries, not top-level),
you cannot accidentally break cond by redefining else since else
is imported from (rnrs) along with cond.

> (does the original described behaviour only occur when redefined
> after a library has been imported?).

Nop. Order does not matter.

> 2. Export all auxiliary keywords, but then I have the problem that
> it will clash with existing keywords, eg 'let' & '=', when imported.

They won't clash if you re-export the 'let' and '=' from (rnrs).
Your library should import and re-export these, rather than define
them as auxiliary keywords.

> Ideally I would like it to be like (1), but without the ability to
> redefine the keywords. Maybe there is a way to achieve this, by
> creating
> the keywords via datum->syntax?

Nop. Don't try.

Abdulaziz Ghuloum (aghuloum) wrote :

On Aug 22, 2008, at 9:15 AM, leppie wrote:

> [(select s into k rest ...) ; select removed 'free-identifier=?'
> matchers
> (eq? (syntax->datum #'select) 'select)
> clauses ...

You can

(define (literal-identifier=? x y)
   (eq? (syntax->datum x) (syntax->datum y)))

and say:
   [(kwd s into k rest ...)
    (literal-identifier=? #'kwd #'select)
    ---]

What binding the keywords give you is the ability to rename and
prefix the auxiliary keywords when they're imported somewhere
else. Using literal-identifier=? hardwires the names.

Binding the auxiliary keywords (instead of leaving them unbound)
has the advantage that the user program would get an expand-time
error if they attempt to redefine the keywords (if they happen
to use libraries and scripts of course; you cannot help it at
the top-level). So, if you leave the auxiliary keywords unbound,
it's better to use literal-identifier=?. A third option is to
use an unbound identifier keyword and raise an exception if you
find it bound in the input of the macro (this is a little extreme
though).

leppie (leppie) wrote :

> They won't clash if you re-export the 'let' and '=' from (rnrs).

Duh! I never thought of that. That makes more sense now.

And thanks a lot for the info on binding. I think I can find an acceptable solution from this :)

Cheers

leppie

leppie (leppie) on 2008-08-22
Changed in ikarus:
status: New → Invalid
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers

Related questions