From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Mark H Weaver Newsgroups: gmane.lisp.guile.devel Subject: Re: Feature request: Expose `ellipsis?' from psyntax.ss Date: Tue, 20 Nov 2018 22:37:45 -0500 Message-ID: <871s7fuml7.fsf@netris.org> 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> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Trace: blaine.gmane.org 1542771395 1869 195.159.176.226 (21 Nov 2018 03:36:35 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Wed, 21 Nov 2018 03:36:35 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux) Cc: guile-devel@gnu.org To: Marc =?utf-8?Q?Nieper-Wi=C3=9Fkirchen?= Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Wed Nov 21 04:36:31 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 1gPJJX-0000O5-9n for guile-devel@m.gmane.org; Wed, 21 Nov 2018 04:36:31 +0100 Original-Received: from localhost ([::1]:37029 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gPJLb-0005ji-WF for guile-devel@m.gmane.org; Tue, 20 Nov 2018 22:38:40 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:47746) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gPJLU-0005bZ-TY for guile-devel@gnu.org; Tue, 20 Nov 2018 22:38:34 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gPJLR-00070l-O4 for guile-devel@gnu.org; Tue, 20 Nov 2018 22:38:32 -0500 Original-Received: from world.peace.net ([64.112.178.59]:51466) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gPJLR-0006yP-K7 for guile-devel@gnu.org; Tue, 20 Nov 2018 22:38:29 -0500 Original-Received: from mhw by world.peace.net with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1gPJLP-00027T-MY; Tue, 20 Nov 2018 22:38:27 -0500 In-Reply-To: ("Marc \=\?utf-8\?Q\?Nieper-Wi\=C3\=9Fkirchen\=22's\?\= message of "Sat, 17 Nov 2018 16:03:42 +0100") X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 64.112.178.59 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:19757 Archived-At: Hi Marc, Marc Nieper-Wi=C3=9Fkirchen writes: > > Let's run the following example: > > > > (eval-when (expand) > > (define-syntax bar > > (syntax-rules () > > ((_ stx) > > (syntax-case stx () > > ((_ a (... ...)) > > #'#t) > > ((_ a b c) > > #'#f)))))) > > > > (define-syntax foo > > (lambda (stx) > > (with-ellipsis e (bar stx)))) > > > > (display (foo 1 2 3)) > > (newline) > > [Note: I fixed the indentation in the definition of 'bar' above, which > was misleading as it appeared in your email.] > > > This one displays `#t' in Guile, which is exactly what we want. I > > guess the reason is that the macro invocation `(bar stx)' creates a > > new transformer environment, in which `{# $sc-ellipsis #}' becomes > > unbound again. > > No, this is not quite right. When the transformer code of 'foo' is > expanded, 'bar' expands into a 'syntax-case' form, and that > 'syntax-case' form is indeed expanded within a transformer environment > that includes the ellipsis binding introduced by the 'with-ellipsis' > form in 'foo'. > > However, all of the bindings in the transformer environment bind > *gensyms*. These gensyms are effectively inaccessible unless the wrap > includes a substitution that maps user-visible identifiers into those > gensyms. > > So I should view the transformer environment as a store, shouldn't I? > During the course of expansion, the transformer environment is > monotonically growing, 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'. > 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 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))) 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. > 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))) =3D> '((1) (2)) > I think so and it also makes sense with respect to the two examples > below. Is this special pseudo-substitution used by any other core form > besides `with-ellipsis'? No, except for 'syntax-case' and 'syntax', which look for the pseudo-substitution while checking for ellipsis identifiers. > > 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=3D?', 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 k= now > > > 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