From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: =?UTF-8?Q?Marc_Nieper=2DWi=C3=9Fkirchen?= Newsgroups: gmane.lisp.guile.devel Subject: Re: Feature request: Expose `ellipsis?' from psyntax.ss Date: Wed, 21 Nov 2018 09:40:36 +0100 Message-ID: References: <875zwzmq4n.fsf@netris.org> <87pnv6iss7.fsf@netris.org> <87k1leip2i.fsf@netris.org> <124085ab-2a76-4892-e790-d58b07bcb3fc@nieper-wisskirchen.de> <87lg5tj3gn.fsf@netris.org> <874lcgzj9p.fsf@netris.org> <871s7fuml7.fsf@netris.org> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="000000000000f6a3ba057b28b4ca" X-Trace: blaine.gmane.org 1542789551 15052 195.159.176.226 (21 Nov 2018 08:39:11 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Wed, 21 Nov 2018 08:39:11 +0000 (UTC) Cc: guile-devel@gnu.org To: mhw@netris.org Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Wed Nov 21 09:39:07 2018 Return-path: Envelope-to: guile-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1gPO2N-0003iC-11 for guile-devel@m.gmane.org; Wed, 21 Nov 2018 09:39:07 +0100 Original-Received: from localhost ([::1]:37824 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gPO4O-0001zO-1i for guile-devel@m.gmane.org; Wed, 21 Nov 2018 03:41:12 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:52396) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gPO45-0001z5-RV for guile-devel@gnu.org; Wed, 21 Nov 2018 03:40:56 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gPO42-00006P-DT for guile-devel@gnu.org; Wed, 21 Nov 2018 03:40:53 -0500 Original-Received: from mail-pl1-f176.google.com ([209.85.214.176]:39696) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gPO42-0008Vg-2O for guile-devel@gnu.org; Wed, 21 Nov 2018 03:40:50 -0500 Original-Received: by mail-pl1-f176.google.com with SMTP id b5-v6so4485288pla.6 for ; Wed, 21 Nov 2018 00:40:49 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=t117sDpwDUnjnjEEHEtxtvhGR3PDbRwvukR2fAaUYno=; b=doHx4KSeC0S+buOXVt+ttCvYJdCtfBdJhvig7d8oTvXeFfW01vPe3UzW5ozIJO8hoY h2lbK3VdQBJOXXk/TcKRZo1zOPAKN8uMkgUSoiBP0r9JjqamqswlJuCz8Hcju21vtPTo vEJea8ERHX3e5vBfNXk4rF8r2zGEVv0ai9/1K1kkUgrr2zpdSjVXpmPFm4SqWcUUcFgX EjPTYLs3qRAFcysrjVoD1cWfn7rxJaOJJwOxIjIVpJGU+EgkmflJX3a7wptQZlND9NvJ fXcl2ws19r1lz1O7sDDFauqRyZEYkPLVfHlbSGU955gW5x+a5tgFt68PYhspRXkzloEM MxKQ== X-Gm-Message-State: AA+aEWYTtAGjhRbaAG+vdP9qY2tmTfv6M4GShQpGy1ErSQDgm2kMgXXG ij/I/o0vzCK5PRhwUXVtK/xP8rkSaWMUuzgYWUOBXtoo X-Google-Smtp-Source: AFSGD/UaG+BXFUG3ho5l5s+WIEICuxr9XULNVNQu2JgXPRJBWB0wn4HHiQeoN7EHeQJweMyifsYIMbHn4HYtdQ47jbI= X-Received: by 2002:a65:50c1:: with SMTP id s1mr4989718pgp.350.1542789648522; Wed, 21 Nov 2018 00:40:48 -0800 (PST) In-Reply-To: <871s7fuml7.fsf@netris.org> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.85.214.176 X-BeenThere: guile-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Developers list for Guile, the GNU extensibility library" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Original-Sender: "guile-devel" Xref: news.gmane.org gmane.lisp.guile.devel:19758 Archived-At: --000000000000f6a3ba057b28b4ca Content-Type: text/plain; charset="UTF-8" Am Mi., 21. Nov. 2018 um 04:38 Uhr schrieb Mark H Weaver : > Hi Marc, > Dear Mark, thank you very much for all your detailed replies; these are extremely helpful! > No, it does not monotonically grow during the course of expansion. One > way to think about it is that it's the lexical environment of the > _expanded_ code. It therefore only grows when the macro expander > _descends_ into a core lexical binding form. For example, in > > (let ((x (let ((y 4)) > (+ y y)))) > (+ x x)) > > the expansion environment used to expand (+ x x) does not include a > binding for the gensym corresponding to 'y'. > Ah, ok. > > In general, that's how Psyntax implements lexical binding. When a core > > binding form is encountered, a fresh gensym is bound in the transformer > > environment, and that new environment is used to expand all forms > > within, including the results of expanding macros within, which in > > general include identifiers that originally appeared in macro > > definitions elsewhere that are not in the lexical scope of those > > bindings. > > > > The reason this works is because when a core binding form is encountered > > by the expander, the fresh gensym is substituted for all free references > > of the user-visible identifier in the body, *before* expanding the > > macros found within. The substitution is deferred using the 'wrap' > > mechanism, but the result is the same. Any identifiers not visible in > > the body at that time are not affected by that subtitution. > > > > Ellipsis identifiers are a bit more tricky, because unlike other > > bindings, the user-visible ellipsis identifiers are not actually > > substituted. We can't do that because ellipsis identifiers can be used > > for other purposes, e.g. bound to ordinary variables or macros, and > > these two ways of binding ellipsis identifiers should not shadow each > > other. > > > > Is this universally true? Maybe I misunderstood what you mean about > > shadowing. How about the following? > > > > (use-modules (guile)) > > (define-syntax ... (syntax-rules ())) > > (define-syntax bar > > (syntax-rules () > > ((_ ...) ...))) > > > > At least by the R7RS, this shouldn't yield an error due to a misplaced > > ellipsis. > > 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. Did you compile Chibi with -DSEXP_USE_STRICT_TOPLEVEL_BINDINGS=1? Without, Chibi does not implement the intended R7RS semantics; see the fourth paragraph here: http://synthcode.com/scheme/chibi/#h3_SchemeStandard. And, indeed, Chibi 0.8 with this feature flag enabled returns '((1) 2) once more. So what does R7RS say about it: `...' is defined as auxiliary syntax in Section 4.3.2. By Section 1.3.3, auxiliary syntax describes a syntax binding. Appendix A. states that the standard binding for `...' is exported by `(scheme base)'. Auxiliary syntax is matched by their binding in R7RS; an example for this is given at the end of Section 4.3.2: The expression `(let ((=> #f)) (cond #t => 'ok))' is not an error, but evaluates to `ok'. Note that this example is about the auxiliary syntax `=>', but the report does not distinguish different types of auxiliary syntax (in particular, it is nowhere mentioned that `...' should be treated differently than any other auxiliary syntax). Thus, Chibi (in R7RS mode by the above flag) implements the R7RS correctly. There has been some discussion about the behavior of auxiliary keywords in the WG1 that created R7RS: https://groups.google.com/forum/#!searchin/scheme-reports-wg1/auxiliary$20syntax%7Csort:date/scheme-reports-wg1/wgwLzo7zivk/pWtlwvLED3UJ. In the end, it was voted in favor of ticket #83 discussed there, which is in accordance to the final report. Now to R6RS. Section 6.4 says that auxiliary syntax describes a syntax binding. Section 12.4 of the R6RS Library Report defines `...' as auxiliary syntax. The example definition of `case' in Section 12.5 ibid. shows explicitely that auxiliary keywords are matched using `free-identifier=?' (in this example, `else'). Also, in all other regards with respect to auxiliary syntax R6RS does not differ from R7RS. Thus, Chez Scheme implements the R6RS semantics. Now, what about Racket in R6RS mode? I tested your examples with Racket 6.12 and the results are the same as in Racket 7.0. The following code prints '((1) (2)): #!r6rs (import (rnrs)) (write (let ((... 'hello)) let-syntax ((foo (syntax-rules () ((foo x ...) '((x) ...))))) (foo 1 2)))) (newline) Does Racket contradict the R6RS semantics here? Not necessarily. The reason is that in Racket identifiers are allowed to have different bindings during different phases, while R6RS does not allow to import the same identifier with different bindings at different phases, for example (the reason is that R6RS also wants to support implicit phasing). So in this example, at phase level 0, `...' is being bound to a location containing the symbol 'hello, while the binding of `...' at phase level 1 is the ellipsis as recognized by `syntax-case'. This behavior is demonstrated by the following example, which prints '((1) 2) in agreement with Chez and the R[67]RS. #!r6rs (import (rnrs)) (write (let-syntax ((foo (let ((... 'hello)) (syntax-rules () ((foo x ...) '((x) ...)))))) (foo 1 2))) (newline) The reason is that the transformer code is expanded and evaluated in phase level 1. Another demonstration of this fact is given by the following, which also prints '((1) 2): #!r6rs (import (rnrs) (only (racket) begin-for-syntax)) (begin-for-syntax (define ... 'hello)) (write (let-syntax ((foo (syntax-rules () ((foo x ...) '((x) ...))))) (foo 1 2))) (newline) > And what about: > > > > (with-ellipsis e > > (define-syntax e (syntax-rules ())) > > (define-syntax bar > > (syntax-rules () > > ---))) > > > > Is `e' recognized as the ellipsis in `---'? > > Yes. For example: > > (with-ellipsis e > (define-syntax e (syntax-rules ())) > (let-syntax ((foo (syntax-rules () > ((foo x e) > '((x) e))))) > (foo 1 2))) > > => '((1) (2)) > This is unfortunate because it contradicts the R7RS semantics discussed above. It also goes against the Scheme (as a LISP-1) tradition that an identifier (at least in models with implicit phasing) has only one binding (e.g. to a variable location, to a syntax transformer, as a pattern variable), doesn't it? Regards, Marc > > Now, why does the following work (i.e. why does it print `#t')? > > > > > > (eval-when (expand) > > > (define-syntax bar2 > > > (syntax-rules () > > > ((_ e body) > > > (with-ellipsis e body))))) > > > > > > (define-syntax foo2 > > > (lambda (stx) > > > (bar2 f (syntax-case stx () > > > ((_ a ...) > > > #'#t) > > > ((_ a b c) > > > #'#f))))) > > > > > > (display (foo2 1 2 3)) > > > (newline) > > > > I think this should print #f, and that's what happens on my machine with > > Guile 2.2.3. In this example, 'bar2' is essentially an alias for > > 'with-ellipsis', and should behave the same way. > > > > I tested several variation of the above example and probably managed > > to confound the results. :-/ Here are hopefully the correct results > > (Guile 2.2.4 as shipped with Ubuntu 18.10): > > > > 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) > > This last example is an interesting case which demonstrates an aspect of > the 'with-ellipsis' semantics that I haven't previously discussed in > this thread. > > When checking if an identifier is an ellipsis, the only 'with-ellipsis' > bindings that will be considered in scope are those where the first > operand to 'with-ellipsis' have the same marks as the identifier being > checked. > > In this case, the '...' identifier in 'foo2' has different marks than > the 'f' in 'bar2', so that ellipsis binding is effectively ignored. > Since there are no other ellipsis bindings in scope, the identifier is > simply compared with '...' using 'free-identifier=?', as the default > ellipsis identifier. > > > I think this behavior of your algorithm and the `with-ellipsis' form > > is optimal as it allows to nested macro expansions to have their > > private ellipsis identifiers while it also allows to write wrapper > > macros that behave like `with-ellipsis'. > > Thanks. I'm not 100% sure these are the ideal semantics, but they seem > to cover the cases I've considered reasonably well. > > > > > In Chez Scheme, I would have used `define-property' to define my > > > > custom property directly on the identifier standing for the pattern > > > > variable. I haven't found an equivalent feature in Guile. I don't > know > > > > how to nicely code my-syntax-case/my-syntax in standard R6RS. > > > > > > Sure, that sounds like a nice feature. I'll add it to my TODO list > :) > > > > > > That would be great! :-) > > > > I'll probably raise the priority of this TODO item, since I'd prefer to > > enable you to avoid using 'syntax-local-binding' if possible. > > > > How would you implement this? > > I'm not yet sure, I would need to study the problem carefully and have > not yet had the time. > > Regards, > Mark > --000000000000f6a3ba057b28b4ca Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Am Mi., 21. Nov. 2018 um 04:38= =C2=A0Uhr schrieb Mark H Weaver <mhw@netris.org>:
Hi Marc,
Dear Mark,

thank you very much for all= your detailed replies; these are extremely helpful!
=C2=A0
No, it does not monot= onically grow during the course of expansion.=C2=A0 One
way to think about it is that it's the lexical environment of the
_expanded_ code.=C2=A0 It therefore only grows when the macro expander
_descends_ into a core lexical binding form.=C2=A0 For example, in

=C2=A0 (let ((x (let ((y 4))
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(+ y y))))
=C2=A0 =C2=A0 (+ x x))

the expansion environment used to expand (+ x x) does not include a
binding for the gensym corresponding to 'y'.
<= br>
Ah, ok.
=C2=A0
>=C2=A0 In general, that's how Psyntax implements lexical binding.= =C2=A0 When a core
>=C2=A0 binding form is encountered, a fresh gensym is bound in the tran= sformer
>=C2=A0 environment, and that new environment is used to expand all form= s
>=C2=A0 within, including the results of expanding macros within, which = in
>=C2=A0 general include identifiers that originally appeared in macro >=C2=A0 definitions elsewhere that are not in the lexical scope of those=
>=C2=A0 bindings.
>
>=C2=A0 The reason this works is because when a core binding form is enc= ountered
>=C2=A0 by the expander, the fresh gensym is substituted for all free re= ferences
>=C2=A0 of the user-visible identifier in the body, *before* expanding t= he
>=C2=A0 macros found within.=C2=A0 The substitution is deferred using th= e 'wrap'
>=C2=A0 mechanism, but the result is the same.=C2=A0 Any identifiers not= visible in
>=C2=A0 the body at that time are not affected by that subtitution.
>
>=C2=A0 Ellipsis identifiers are a bit more tricky, because unlike other=
>=C2=A0 bindings, the user-visible ellipsis identifiers are not actually=
>=C2=A0 substituted.=C2=A0 We can't do that because ellipsis identif= iers can be used
>=C2=A0 for other purposes, e.g. bound to ordinary variables or macros, = and
>=C2=A0 these two ways of binding ellipsis identifiers should not shadow= each
>=C2=A0 other.
>
> Is this universally true? Maybe I misunderstood what you mean about > shadowing. How about the following?
>
> (use-modules (guile))
> (define-syntax ... (syntax-rules ()))
> (define-syntax bar
>=C2=A0 =C2=A0(syntax-rules ()
>=C2=A0 =C2=A0 =C2=A0((_ ...) ...)))
>
> At least by the R7RS, this shouldn't yield an error due to a mispl= aced
> ellipsis.

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.=C2=A0 The Scheme implementations I tried do not seem to agree.
For example, consider this example:

=C2=A0 (let ((... 'hello))
=C2=A0 =C2=A0 (let-syntax ((foo (syntax-rules ()
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 ((foo x ...)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0'((x) ...)))))
=C2=A0 =C2=A0 =C2=A0 (foo 1 2)))

If '...' is recognized as an ellipsis within the 'let', the= n the result
will be '((1) (2)).=C2=A0 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).
=C2=A0
I found that Racket 7.0, Chicken 4.13.0, and Scheme48 1.9.2 return
'((1) (2)).=C2=A0 Chibi-Scheme returns '((1) 2).=C2=A0 I see the sa= me results
with this variant:

=C2=A0 (let-syntax ((... (syntax-rules ())))
=C2=A0 =C2=A0 (let-syntax ((foo (syntax-rules ()
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 ((foo x ...)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0'((x) ...)))))
=C2=A0 =C2=A0 =C2=A0 (foo 1 2)))

Again, Chez= returns '((1) 2).


If we instead bind '...' as a top-level variable:

=C2=A0 (define-syntax ... (syntax-rules ()))
=C2=A0 (let-syntax ((foo (syntax-rules ()
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 ((foo x ...)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0'((x) ...)))))
=C2=A0 =C2=A0 (foo 1 2))

Then all four of these Schemes agree that the answer is '((1) (2)),
including Chibi-Scheme.

Chez Scheme still re= turns '((1) 2) and thus does not recognize `...' as the ellipsis. <= br>

Di= d you compile Chibi with -DSEXP_USE_STRICT_TOPLEVEL_BINDINGS=3D1? Without, = Chibi does not implement the intended R7RS semantics; see the fourth paragr= aph here: http://synthcode.com/scheme/chibi/#h3_SchemeStandard.= And, indeed, Chibi 0.8 with this feature flag enabled returns '((1) 2)= once more.

So what does R7RS say about it: `...' is defined as auxiliary= syntax in Section 4.3.2. By Section 1.3.3, auxiliary syntax describes a sy= ntax binding. Appendix A. states that the standard binding for `...' is= exported by `(scheme base)'. Auxiliary syntax is matched by their bind= ing in R7RS; an example for this is given at the end of Section 4.3.2: The = expression `(let ((=3D> #f)) (cond #t =3D> 'ok))' is not an e= rror, but evaluates to `ok'. Note that this example is about the auxili= ary syntax `=3D>', but the report does not distinguish different typ= es of auxiliary syntax (in particular, it is nowhere mentioned that `...= 9; should be treated differently than any other auxiliary syntax).

Thus, Chibi (i= n R7RS mode by the above flag) implements the R7RS correctly.

There has been= some discussion about the behavior of auxiliary keywords in the WG1 that c= reated R7RS: https://groups.google.com/forum/#!searchin/sc= heme-reports-wg1/auxiliary$20syntax%7Csort:date/scheme-reports-wg1/wgwLzo7z= ivk/pWtlwvLED3UJ. In the end, it was voted in favor of ticket #83 discu= ssed there, which is in accordance to the final report.

Now to R6RS. Section = 6.4 says that auxiliary syntax describes a syntax binding. Section 12.4 of = the R6RS Library Report defines `...' as auxiliary syntax. The example = definition of `case' in Section 12.5 ibid. shows explicitely that auxil= iary keywords are matched using `free-identifier=3D?' (in this example,= `else'). Also, in all other regards with respect to auxiliary syntax R= 6RS does not differ from R7RS.

Thus, Chez Scheme implements the R6RS semantics.

Now, wh= at about Racket in R6RS mode? I tested your examples with Racket 6.12 and t= he results are the same as in Racket 7.0. The following code prints '((= 1) (2)):

#!r6rs
(import (rnrs)= )

(write
=C2=A0 (let ((... 'hello))
=C2=A0 =C2=A0=C2=A0 le= t-syntax ((foo (syntax-rules ()
=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 ((foo x ...)
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0 '((x) ...)))))
=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0 (foo 1 2))))
(newline)

Does Racket contradict the R6RS semantics he= re? Not necessarily. The reason is that in Racket identifiers are allowed t= o have different bindings during different phases, while R6RS does not allo= w to import the same identifier with different bindings at different phases= , for example (the reason is that R6RS also wants to support implicit phasi= ng). So in this example, at phase level 0, `...' is being bound to a lo= cation containing the symbol 'hello, while the binding of `...' at = phase level 1 is the ellipsis as recognized by `syntax-case'.

This behavior i= s demonstrated by the following example, which prints '((1) 2) in agree= ment with Chez and the R[67]RS.

#!r6rs
(import (rnrs))

(write=C2=A0 (let-syntax ((foo (let ((... 'hello))
=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0 (syntax-rules ()
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0 ((foo x ...)
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 '((x) ...))))))
=C2=A0=C2=A0 (foo = 1 2)))
(newline)

The reason is that the transformer code is expande= d and evaluated in phase level 1.

<= /div>
Another demonstration of this fact is given= by the following, which also prints '((1) 2):

#!r6rs
(import (rnrs) (only (racket) begi= n-for-syntax))

(begin-for-syntax
=C2=A0 (define ... = 'hello))

(write
=C2=A0 (let-syntax ((foo (syntax-rules ()
= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 ((foo x ...)
=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 '((x) ...)))))
= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (foo 1 2)))
(newline)

> And what about:
>
> (with-ellipsis e
>=C2=A0 =C2=A0(define-syntax e (syntax-rules ()))
>=C2=A0 =C2=A0(define-syntax bar
>=C2=A0 =C2=A0 =C2=A0(syntax-rules ()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0---)))
>
> Is `e' recognized as the ellipsis in `---'?

Yes.=C2=A0 For example:

=C2=A0 (with-ellipsis e
=C2=A0 =C2=A0 (define-syntax e (syntax-rules ()))
=C2=A0 =C2=A0 (let-syntax ((foo (syntax-rules ()
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 ((foo x e)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0'((x) e)))))
=C2=A0 =C2=A0 =C2=A0 (foo 1 2)))

=C2=A0 =3D> '((1) (2))

This is u= nfortunate because it contradicts the R7RS semantics discussed above. It al= so goes against the Scheme (as a LISP-1) tradition that an identifier (at l= east in models with implicit phasing) has only one binding (e.g. to a varia= ble location, to a syntax transformer, as a pattern variable), doesn't = it?

Regards,

Marc
=

>= =C2=A0 > Now, why does the following work (i.e. why does it print `#t= 9;)?
>=C2=A0 >
>=C2=A0 > (eval-when (expand)
>=C2=A0 >=C2=A0 =C2=A0(define-syntax bar2
>=C2=A0 >=C2=A0 =C2=A0 =C2=A0(syntax-rules ()
>=C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0((_ e body)
>=C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 (with-ellipsis e body)))))
>=C2=A0 >
>=C2=A0 > (define-syntax foo2
>=C2=A0 >=C2=A0 =C2=A0(lambda (stx)
>=C2=A0 >=C2=A0 =C2=A0 =C2=A0(bar2 f (syntax-case stx ()
>=C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((_ a= ...)
>=C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #= 9;#t)
>=C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((_ a= b c)
>=C2=A0 >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #= 9;#f)))))
>=C2=A0 >
>=C2=A0 > (display (foo2 1 2 3))
>=C2=A0 > (newline)
>
>=C2=A0 I think this should print #f, and that's what happens on my = machine with
>=C2=A0 Guile 2.2.3.=C2=A0 In this example, 'bar2' is essentiall= y an alias for
>=C2=A0 'with-ellipsis', and should behave the same way.
>
> I tested several variation of the above example and probably managed > to confound the results. :-/ Here are hopefully the correct results > (Guile 2.2.4 as shipped with Ubuntu 18.10):
>
> So this one indeed outputs #f, thus reproducing your result.
>
> (eval-when (expand)
>=C2=A0 =C2=A0(define-syntax bar2
>=C2=A0 =C2=A0 =C2=A0(syntax-rules ()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0((_ e body)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 (with-ellipsis e body)))))
>
> (define-syntax foo2
>=C2=A0 =C2=A0(lambda (stx)
>=C2=A0 =C2=A0 =C2=A0(bar2 e (syntax-case stx ()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0((_a ...)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 #'#t)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0((_ a b c)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 #'#f)))))
>
> (display (foo2 1 2 3))
> (newline)
>
> On the other hand, this one prints #t.
>
> (eval-when (expand)
>=C2=A0 =C2=A0(define-syntax bar2
>=C2=A0 =C2=A0 =C2=A0(syntax-rules ()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0((_ e body)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 (with-ellipsis f body))))) ; THE DIFFERENCE= IS HERE.
>
> (define-syntax foo2
>=C2=A0 =C2=A0(lambda (stx)
>=C2=A0 =C2=A0 =C2=A0(bar2 e (syntax-case stx ()
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((_a ...)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'#t)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0((_ a b c)
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 #'#f))))) >
> (display (foo2 1 2 3))
> (newline)

This last example is an interesting case which demonstrates an aspect of the 'with-ellipsis' semantics that I haven't previously discuss= ed in
this thread.

When checking if an identifier is an ellipsis, the only 'with-ellipsis&= #39;
bindings that will be considered in scope are those where the first
operand to 'with-ellipsis' have the same marks as the identifier be= ing
checked.

In this case, the '...' identifier in 'foo2' has different = marks than
the 'f' in 'bar2', so that ellipsis binding is effectively = ignored.
Since there are no other ellipsis bindings in scope, the identifier is
simply compared with '...' using 'free-identifier=3D?', as = the default
ellipsis identifier.

> I think this behavior of your algorithm and the `with-ellipsis' fo= rm
> is optimal as it allows to nested macro expansions to have their
> private ellipsis identifiers while it also allows to write wrapper
> macros that behave like `with-ellipsis'.

Thanks.=C2=A0 I'm not 100% sure these are the ideal semantics, but they= seem
to cover the cases I've considered reasonably well.

>=C2=A0 >=C2=A0 > In Chez Scheme, I would have used `define-proper= ty' to define my
>=C2=A0 >=C2=A0 > custom property directly on the identifier stand= ing for the pattern
>=C2=A0 >=C2=A0 > variable. I haven't found an equivalent feat= ure in Guile. I don't know
>=C2=A0 >=C2=A0 > how to nicely code my-syntax-case/my-syntax in s= tandard R6RS.
>=C2=A0 >
>=C2=A0 >=C2=A0 Sure, that sounds like a nice feature.=C2=A0 I'll= add it to my TODO list :)
>=C2=A0 >
>=C2=A0 > That would be great! :-)
>
>=C2=A0 I'll probably raise the priority of this TODO item, since I&= #39;d prefer to
>=C2=A0 enable you to avoid using 'syntax-local-binding' if poss= ible.
>
> How would you implement this?

I'm not yet sure, I would need to study the problem carefully and have<= br> not yet had the time.

=C2=A0 =C2=A0 =C2=A0Regards,
=C2=A0 =C2=A0 =C2=A0 =C2=A0Mark

--000000000000f6a3ba057b28b4ca--