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: Fri, 23 Nov 2018 22:28:41 +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> <87a7lzpmly.fsf@netris.org> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="00000000000087403c057b5babfd" X-Trace: blaine.gmane.org 1543008435 14153 195.159.176.226 (23 Nov 2018 21:27:15 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Fri, 23 Nov 2018 21:27:15 +0000 (UTC) Cc: guile-devel@gnu.org To: mhw@netris.org Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Fri Nov 23 22:27:11 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 1gQIyl-0003ZD-1q for guile-devel@m.gmane.org; Fri, 23 Nov 2018 22:27:11 +0100 Original-Received: from localhost ([::1]:54326 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gQJ0r-0006Lm-6f for guile-devel@m.gmane.org; Fri, 23 Nov 2018 16:29:21 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:50541) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gQJ0V-0006Le-Rb for guile-devel@gnu.org; Fri, 23 Nov 2018 16:29:01 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gQJ0R-0004Aa-Li for guile-devel@gnu.org; Fri, 23 Nov 2018 16:28:59 -0500 Original-Received: from mail-pl1-f179.google.com ([209.85.214.179]:39405) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gQJ0R-00049r-AV for guile-devel@gnu.org; Fri, 23 Nov 2018 16:28:55 -0500 Original-Received: by mail-pl1-f179.google.com with SMTP id 101so4814376pld.6 for ; Fri, 23 Nov 2018 13:28:55 -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=xcTbN8YA9w/nrV+zsyYyjzqzFMrOSV83OJ0S7rMHuC4=; b=RlpWYk9SocVBXrtWwlueSrk+gZkogIgpo7taUUH0WvxRhR8q88FLVeA/f3bJlMY8bR ZJhWeM0uAaygvbfNdY/6V330DyGvgnONDS3Z4jD3zggvGCFhleky6OdQkvXJDssJlzEl v5OlEaQHEIPQevts1bC2YK3P5Ri7ApP965Zj0wxB1mT6fYPtwpIx3Edi0+QZfHoWsn52 FtHh0fxnz3OYGRWj1F3FL6UpMqlG3TbVzYtZyvZn0oLjZ8hGvuk8Esr1TXKzR8L1tqYM +XtR5WYXjuoxaKgQw+eh3ZYyIKAxOVttFP05dZ32klUQAGVOwFoJZ4yh7JD7I7/tkKya OKaQ== X-Gm-Message-State: AA+aEWY71rv9rkNsJfsc/SPQWu1ZOlEwtNKeryRYmbht6kf1VT5w+MU6 rx4p2xZu95HNIRbcJyp4DO683FiUI7AkepIyx4i/JXiG/ic= X-Google-Smtp-Source: AFSGD/UVU+th1pJ+2Q6cWpipCGBC1pUTFQRpVWnuEoQS3m99JoI5IZBPZcHxcPdhawy0C7Mx+p8eE0KclpCKoXEDp84= X-Received: by 2002:a17:902:e101:: with SMTP id cc1-v6mr17687398plb.165.1543008533559; Fri, 23 Nov 2018 13:28:53 -0800 (PST) In-Reply-To: <87a7lzpmly.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.179 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:19763 Archived-At: --00000000000087403c057b5babfd Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Mark, Am Fr., 23. Nov. 2018 um 21:26 Uhr schrieb Mark H Weaver : > Hi Marc, > > Marc Nieper-Wi=C3=9Fkirchen writes: > > > Am Mi., 21. Nov. 2018 um 04:38 Uhr schrieb Mark H Weaver >: > > > > 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 resul= t > > 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=3D?' 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=3D?' 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=3D?' to check fo= r > the default ellipsis '...'. Using `free-identifier=3D?' to check for the default ellipsis is definitely correct. `bound-identifier=3D?' 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=3D?' but not `bound-identifier=3D?'. > Within the lexical scope of > 'with-ellipsis', Guile uses 'bound-identifier=3D?' 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=3D?' 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=3D?' in scopes inside `with-ellipsis' and `free-identifier=3D?' in scopes outside. > 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 --00000000000087403c057b5babfd Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Mark,

Am Fr., 23. Nov. 2018 um 21:26=C2=A0Uhr schrieb Mark H Weaver <mhw@netris.org>:
Hi Marc,

Marc Nieper-Wi=C3=9Fkirchen <marc@nieper-wisskirchen.de> writes:

> Am Mi., 21. Nov. 2018 um 04:38 Uhr schrieb Mark H Weaver <mhw@netris.org>:
>
>=C2=A0 I'm not aware of any language in the R[567]RS that makes it = clear
>=C2=A0 whether '...' should be recognized as an ellipsis if it = is bound to a
>=C2=A0 variable.=C2=A0 The Scheme implementations I tried do not seem t= o agree.
>
>=C2=A0 For example, consider this example:
>
>=C2=A0 =C2=A0 (let ((... 'hello))
>=C2=A0 =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 =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'((x) ...)))))
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 (foo 1 2)))
>
>=C2=A0 If '...' is recognized as an ellipsis within the 'le= t', then the result
>=C2=A0 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
>=C2=A0 I found that Racket 7.0, Chicken 4.13.0, and Scheme48 1.9.2 retu= rn
>=C2=A0 '((1) (2)).=C2=A0 Chibi-Scheme returns '((1) 2).=C2=A0 I= see the same results
>=C2=A0 with this variant:
>
>=C2=A0 =C2=A0 (let-syntax ((... (syntax-rules ())))
>=C2=A0 =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 =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'((x) ...)))))
>=C2=A0 =C2=A0 =C2=A0 =C2=A0 (foo 1 2)))
>
> Again, Chez returns '((1) 2).
>
>=C2=A0 If we instead bind '...' as a top-level variable:
>
>=C2=A0 =C2=A0 (define-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))
>
>=C2=A0 Then all four of these Schemes agree that the answer is '((1= ) (2)),
>=C2=A0 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 o= f
'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=3D?' to compare identifiers with #'(... .= ..) in
psyntax.scm, where '...' has no binding.=C2=A0 In the examples abov= e, the
ellipsis identifiers are within a lexical environment where '...' i= s
bound, so 'free-identifier=3D?' 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=3D?' t= o check for
the default ellipsis '...'.=C2=A0

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

(= import (scheme base))

(define-syntax foo
=C2=A0 (syntax-rules ()
=C2=A0 =C2=A0 ((foo e) TEMPLATE)))
=

(foo ...)

In TEMPLATE, both `.= ..' and `e' have to be the ellipsis. They are `free-identifier=3D?&= #39; but not `bound-identifier=3D?'.
=C2=A0
Within the lexical scope of
'with-ellipsis', Guile uses 'bound-identifier=3D?' 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=3D?' 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=3D?' in scopes inside `with-ellipsis' and `= free-identifier=3D?' in scopes outside.=C2=A0
=C2=A0
What do you think?

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

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

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

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

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

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

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

(eval-when (expand)
=C2=A0 (define-synta= x bar2
=C2=A0 =C2=A0 (syntax-rules ()
=C2=A0 =C2=A0 =C2=A0 ((_ e body)
=C2=A0 =C2=A0 =C2=A0 = =C2=A0(with-ellipsis e body)))))

(define-syntax foo2
<= div>=C2=A0 (lambda (stx)
=C2=A0 =C2=A0 (bar2 e (syntax-case stx ()<= /div>
=C2= =A0 =C2=A0 =C2=A0 ((_a ...)
<= div> =C2=A0 =C2=A0 =C2=A0 =C2=A0#'#t)
=C2=A0 =C2=A0 =C2=A0 ((_ a b c)<= /div>
=C2=A0 =C2=A0 =C2= =A0 =C2=A0#'#f)))))

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

On th= e other hand, this one prints #t.

(eval-when (expand)
<= div class=3D"gmail_quote">
=C2=A0 (define-syntax ba= r2
=C2=A0 =C2=A0 (syntax-rules ()
=C2=A0 =C2=A0 =C2=A0 ((_ = e body)
=C2=A0 =C2=A0 =C2=A0 =C2=A0(with-ellip= sis f body))))) ; THE DIFFERENCE IS HERE.

=
(define-syntax foo2
=C2=A0 (lambda (s= tx)
=C2=A0 =C2=A0 (bar2 e (syntax-case stx ()<= /div>
=C2=A0 =C2=A0 = =C2=A0 ((_a ...)
=C2=A0 =C2= =A0 =C2=A0 =C2=A0#'#t)
<= font face=3D"monospace, monospace"> =C2=A0 =C2=A0 =C2=A0 ((_ a b c)
=C2=A0 =C2=A0 =C2=A0 =C2=A0#'#f)))))
=

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

I think this would be a good move. Internal = names shouldn't be forgeable by user code. One may think that no one wo= uld name their identifiers `{# $sc-ellipsis }#', but these kind of issu= es can and do happen in meta-compiling, code reflection, etc.
=C2= =A0
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.

<= div>Such an approach could be generalized to an API that allows to attach c= ustom properties to a lexical scope.
=C2=A0
I welcome input on these questions.

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

-- Marc

= --00000000000087403c057b5babfd--