guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Feature request: Expose `ellipsis?' from psyntax.ss


From: Marc Nieper-Wißkirchen
Subject: Re: Feature request: Expose `ellipsis?' from psyntax.ss
Date: Sat, 24 Nov 2018 10:08:55 +0100

Am Fr., 23. Nov. 2018 um 22:28 Uhr schrieb Marc Nieper-Wißkirchen <address@hidden>:
Hi Mark,

Am Fr., 23. Nov. 2018 um 21:26 Uhr schrieb Mark H Weaver <address@hidden>:
Hi Marc,

Marc Nieper-Wißkirchen <address@hidden> writes:

> Am Mi., 21. Nov. 2018 um 04:38 Uhr schrieb Mark H Weaver <address@hidden>:
>
>  I'm not aware of any language in the R[567]RS that makes it clear
>  whether '...' should be recognized as an ellipsis if it is bound to a
>  variable.  The Scheme implementations I tried do not seem to agree.
>
>  For example, consider this example:
>
>    (let ((... 'hello))
>      (let-syntax ((foo (syntax-rules ()
>                          ((foo x ...)
>                           '((x) ...)))))
>        (foo 1 2)))
>
>  If '...' is recognized as an ellipsis within the 'let', then the result
>  will be '((1) (2)).  Otherwise, the result will be '((1) 2).
>
> I just tested with Chez Scheme 9.5 as well. It returns the same result as Chibi, namely '((1) 2).

>  I found that Racket 7.0, Chicken 4.13.0, and Scheme48 1.9.2 return
>  '((1) (2)).  Chibi-Scheme returns '((1) 2).  I see the same results
>  with this variant:
>
>    (let-syntax ((... (syntax-rules ())))
>      (let-syntax ((foo (syntax-rules ()
>                          ((foo x ...)
>                           '((x) ...)))))
>        (foo 1 2)))
>
> Again, Chez returns '((1) 2).
>
>  If we instead bind '...' as a top-level variable:
>
>    (define-syntax ... (syntax-rules ()))
>    (let-syntax ((foo (syntax-rules ()
>                        ((foo x ...)
>                         '((x) ...)))))
>      (foo 1 2))
>
>  Then all four of these Schemes agree that the answer is '((1) (2)),
>  including Chibi-Scheme.
>
> Chez Scheme still returns '((1) 2) and thus does not recognize `...'
> as the ellipsis.

I stand by the analysis in my previous response in this thread, but
nonetheless I've since realized that given the current implementation of
'ellipsis?' in psyntax.scm, Guile *should* be returning '((1) 2) in
these examples above, and I'm not sure why that's not happening.

The reason is that when no ellipsis binding is present, Guile currently
uses 'free-identifier=?' to compare identifiers with #'(... ...) in
psyntax.scm, where '...' has no binding.  In the examples above, the
ellipsis identifiers are within a lexical environment where '...' is
bound, so 'free-identifier=?' should be returning #false in these cases.

Having said this, given the analysis in my previous response, I'm now
wondering whether it's a mistake to use 'free-identifier=?' to check for
the default ellipsis '...'. 

Using `free-identifier=?' to check for the default ellipsis is definitely correct. `bound-identifier=?' would not give the correct results in R[67]RS because the ellipsis can be imported under several names. Or use a macro that produces aliases of the ellipsis:

(import (scheme base))

(define-syntax foo
  (syntax-rules ()
    ((foo e) TEMPLATE)))

(foo ...)

In TEMPLATE, both `...' and `e' have to be the ellipsis. They are `free-identifier=?' but not `bound-identifier=?'.
 
Within the lexical scope of
'with-ellipsis', Guile uses 'bound-identifier=?' to check for the
user-defined ellipsis, but perhaps we should be using it uniformly to
check for ellipsis in all cases.

Using `free-identifier=?' would only make sense in a model, in which `(with-ellipsis E BODY)' actually binds `E', wouldn't it? In any case, inside the scope of `with-ellipsis', the behavior of `syntax-case', `syntax' differs substantially with respect to the ellipsis from the behavior outside of `with-ellipsis'. Thus it coud very well be that we need `bound-identifier=?' in scopes inside `with-ellipsis' and `free-identifier=?' in scopes outside. 

P.S.:

Thought about it a bit more. I think it would work if we used `free-identifier=?' everywhere, which means: `(with-ellipsis E BODY)' stores the current binding of `E' in CURRENT-ELLIPSIS-BINDING. In body, `(ellipsis-identifier? x)' then compares the binding of `x' with CURRENT-ELLIPSIS-BINDING.

This should be compatible with SRFI-46/R7RS: `(syntax-rules ::: (<id> ---) <rule> ---)' would be rewritten as `(let-syntax ((::: (auxiliary-syntax))) (with-ellpsis ::: (syntax-rules (<id> ...) <rule> ---)'.

Does this make sense? 

-- Marc

 
What do you think?

Also, for the sake of completeness I should explain a remaining detail
of Guile's semantics for 'with-ellipsis' bindings.  In my previous
response, I wrote:

>  I think it makes more sense to model (with-ellipsis E BODY) as
>  introducing a new lexical binding with a fixed name.  CURRENT-ELLIPSIS
>  would be a good name if we wanted to make it part of the public API.  In
>  this model, 'E' becomes the _value_ of the binding, instead of the
>  binding's name.

In existing versions of Guile, the special fixed name is equivalent to:

  (datum->syntax E '#{ $sc-ellipsis }#)

Where E is the identifier being bound or checked.  In particular, the
_marks_ from E are copied to the special fixed name, both when binding a
new ellipsis identifier and when checking for ellipsis identifier.  In
other words, for each set of marks there's a distinct ellipsis binding.

That is actually good for hygiene as the following two examples (from an earlier post in this thread) show:

So this one indeed outputs #f, thus reproducing your result.

(eval-when (expand)
  (define-syntax bar2
    (syntax-rules ()
      ((_ e body)
       (with-ellipsis e body)))))

(define-syntax foo2
  (lambda (stx)
    (bar2 e (syntax-case stx ()
      ((_a ...)
       #'#t)
      ((_ a b c)
       #'#f)))))

(display (foo2 1 2 3))
(newline)

On the other hand, this one prints #t.

(eval-when (expand)
  (define-syntax bar2
    (syntax-rules ()
      ((_ e body)
       (with-ellipsis f body))))) ; THE DIFFERENCE IS HERE.

(define-syntax foo2
  (lambda (stx)
    (bar2 e (syntax-case stx ()
      ((_a ...)
       #'#t)
      ((_ a b c)
       #'#f)))))

(display (foo2 1 2 3))
(newline)
 
WARNING: Note that the name #{ $sc-ellipsis }# is not part of Guile's
API, and is subject to change.  We reserve the right to change its name,
or to make it completely inaccessible in future versions of Guile. 

I think this would be a good move. Internal names shouldn't be forgeable by user code. One may think that no one would name their identifiers `{# $sc-ellipsis }#', but these kind of issues can and do happen in meta-compiling, code reflection, etc.
 
For example, we might move the ellipsis bindings out of the substitution and
into a separate field in the wrap which maps marks to ellipsis
identifiers, with no name involved at all.

Such an approach could be generalized to an API that allows to attach custom properties to a lexical scope.
 
I welcome input on these questions.

Anytime, although I should mention that I am not an expert.

-- Marc



--
Prof. Dr. Marc Nieper-Wißkirchen
 
Universität Augsburg
Institut für Mathematik
Universitätsstraße 14
86159 Augsburg
 
Tel: 0821/598-2146
Fax: 0821/598-2090
 
E-Mail: address@hidden
Web: www.math.uni-augsburg.de/alg/mitarbeiter/mnieper/

reply via email to

[Prev in Thread] Current Thread [Next in Thread]