From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: Zelphir Kaltstahl Newsgroups: gmane.lisp.guile.user Subject: Re: Threading / Pipe Macro (Chris Vine, Mark H Weaver) Date: Mon, 8 Jul 2019 23:10:28 +0200 Message-ID: <94d91369-f127-640e-a6e5-7408618f21c1@gmail.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="226347"; mail-complaints-to="usenet@blaine.gmane.org" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.7.1 To: guile-user@gnu.org Original-X-From: guile-user-bounces+guile-user=m.gmane.org@gnu.org Mon Jul 08 23:21:42 2019 Return-path: Envelope-to: guile-user@m.gmane.org Original-Received: from lists.gnu.org ([209.51.188.17]) by blaine.gmane.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1hkb4u-000wTD-JP for guile-user@m.gmane.org; Mon, 08 Jul 2019 23:21:40 +0200 Original-Received: from localhost ([::1]:44774 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hkavI-0003Ln-5y for guile-user@m.gmane.org; Mon, 08 Jul 2019 17:11:44 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:60367) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hkauM-0003Lg-Ih for guile-user@gnu.org; Mon, 08 Jul 2019 17:10:48 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hkauJ-0000Sv-MA for guile-user@gnu.org; Mon, 08 Jul 2019 17:10:45 -0400 Original-Received: from mail-wr1-x42b.google.com ([2a00:1450:4864:20::42b]:46804) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hkauE-0000Og-W7 for guile-user@gnu.org; Mon, 08 Jul 2019 17:10:39 -0400 Original-Received: by mail-wr1-x42b.google.com with SMTP id z1so14011727wru.13 for ; Mon, 08 Jul 2019 14:10:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:to:references:from:message-id:date:user-agent:mime-version :in-reply-to:content-transfer-encoding:content-language; bh=MSwpE8ifmr62e4nvl/dZeEfh0gM9wGycXoYpSa/ltnY=; b=KMAhs+WlWyKcVy0OxX4Y/JDHxlxkVEFxWkkzz4buBPiB/MoQemCDBsoB+aKfJr2NkV /Exnuj3MbPJfdyxLDOPobPu8xlJ7Xu+PV4ibG3OxCGpwPNDsdVGDPI8K6s4Va4igwuMk w6lYPKGludyeCqux4S5gjPy4lZhOvndiKuC4V1my3lp87SMecappvhYGbr3yVZdIPvPJ pGADWPZN4CP/dInglpvNE+KrEwM0d4T4l7NP9nymGsc61CPuGBT4vJQrzXpRmlDKuNfy zHBv+XPDHK/2oziu0xb46O+db4hwIU5n3wX1vzgbOy7LZM+TSW3mMXeCdVCwBzQHRko6 bhqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-transfer-encoding :content-language; bh=MSwpE8ifmr62e4nvl/dZeEfh0gM9wGycXoYpSa/ltnY=; b=nnXAQRoYmGSKSgeWbrijUeOi5Q0YxYKhb1oWTDBfJkkuWc/QPG7GnjOJjD1uSOvBZ9 orD0pa24wF+Cx6/SHYC/w0tHeC2TLEejJzJCAszTiiotS2W7XmjQsmAefc5FEJ56wEDK 6Oc7xDDoEDzkWyYAnfGlyd5u+/Xz0T5o/hsSfgwJmZEyyvHgoVIRU8idw3q/Q1AR573G iVsesbdBRsAlzLqShWe5TY1049PgYwFlPCf2FnY0S1wN2Kipm0iC3LjNkUpHX7A2j8DE 2Qr5Lth3cdBONzguzz0OdBlu4h1yU0fTVgM4hBxUK3UsYg9FvfhhNvi+jY8KT4oZdEf9 Nd3g== X-Gm-Message-State: APjAAAVqISEG40FObUAUCfuugMI897kzQUDfNXrfC5c5aeT7pT9T5cGt +pOJvEBv0XxhildM2AzaOsOvqTmGs8Y= X-Google-Smtp-Source: APXvYqwfQLCj3p0DuPgY8bQsTTw19fyk+OtTcrzMmwXgymznCuTmmOSOkjvE0HM8AkqJxyih1akaGA== X-Received: by 2002:a5d:494d:: with SMTP id r13mr21395162wrs.152.1562620231552; Mon, 08 Jul 2019 14:10:31 -0700 (PDT) Original-Received: from [10.9.0.5] ([185.65.134.166]) by smtp.googlemail.com with ESMTPSA id c1sm33743145wrh.1.2019.07.08.14.10.29 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 08 Jul 2019 14:10:30 -0700 (PDT) In-Reply-To: Content-Language: en-US X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::42b X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-user-bounces+guile-user=m.gmane.org@gnu.org Original-Sender: "guile-user" Xref: news.gmane.org gmane.lisp.guile.user:15615 Archived-At: Hello Chris and hello Mark, Thank you both for posting your macros! This is really useful and I am looking forward to using this in the next situation where there would be deep nesting or where it seems appropriate in other ways. To understand what is going on in the macro, I wrote a lot of explaining comments and uploaded it in a new repository, where I intend to keep macros I come across and explain them, if I can: https://gitlab.com/ZelphirKaltstahl/guile-scheme-macros/blob/master/threading-pipe-macro/macro.scm If there are mistakes in my explanations, it would be great if you could point out the mistakes : ) I also think it should be easily doable for me to change for example where the piped argument is inserted in the list of arguments. Instead of `append` I could probably put the list built by recurring in between the `func` and `args` to insert in the first position. Not sure what kind of license I should put on it (?) Usually I put everything under GPL, but I am not sure how it is when the code is actually from someone else. Thanks! Zelphir On 7/8/19 12:26 AM, guile-user-request@gnu.org wrote: > Message: 4 > Date: Sun, 7 Jul 2019 21:16:13 +0100 > From: Chris Vine > To: Mark H Weaver > Cc: guile-user@gnu.org > Subject: Re: Threading / Pipe Macro > Message-ID: <20190707211613.4a8a637da93e592b4f737a7f@gmail.com> > Content-Type: text/plain; charset=US-ASCII > > On Sun, 07 Jul 2019 15:30:36 -0400 > Mark H Weaver wrote: >> Hi Chris, >> >> Chris Vine writes: >> >>> I have a pipeline macro which sort-of mimics ML's |> pipeline operator >>> which I use a lot: >>> >>> (define-syntax -> >>> (lambda (x) >>> (syntax-case x () >>> [(k exp0 . exps) >>> (let* ([reversed (reverse (cons (syntax->datum #'exp0) >>> (syntax->datum #'exps)))] >>> [out (let loop ([first (car reversed)] >>> [rest (cdr reversed)]) >>> (if (null? rest) >>> first >>> (let ([func (car first)] >>> [args (cdr first)]) >>> (append `(,func ,@args) >>> (list (loop (car rest) (cdrrest)))))))]) >>> (datum->syntax #'k out))]))) >>> >>> Because all the macro does is to rearrange input forms, this is hygienic >>> without the need to manipulate syntax objects - you can convert to a datum, >>> rearrange and then convert back to a syntax object again, as above. >> This macro is *not* hygienic. The calls to 'syntax->datum' strip all of >> the context information from the syntax objects, and then build a new >> expression using raw S-expressions. The result is essentially the same >> as if you used 'define-macro'. This results various problems. >> >> For example: >> >> --8<---------------cut here---------------start------------->8--- >> scheme@(guile-user)> (define-syntax -> >> (lambda (x) >> (syntax-case x () >> [(k exp0 . exps) >> (let* ([reversed (reverse (cons (syntax->datum #'exp0) >> (syntax->datum #'exps)))] >> [out (let loop ([first (car reversed)] >> [rest (cdr reversed)]) >> (if (null? rest) >> first >> (let ([func (car first)] >> [args (cdr first)]) >> (append `(,func ,@args) >> (list (loop (car rest) (cdr rest)))))))]) >> (datum->syntax #'k out))]))) >> scheme@(guile-user)> (define t 'global-t) >> scheme@(guile-user)> (define-syntax-rule (foo x) >> (-> x (format #t "[t=~A] ~A\n" t))) >> scheme@(guile-user)> (let ((t 'inner-t)) (foo t)) >> [t=global-t] global-t >> $1 = #t >> scheme@(guile-user)> >> --8<---------------cut here---------------end--------------->8--- >> >> I recommend reformulating the -> macro using 'syntax-rules' as follows: >> >> --8<---------------cut here---------------start------------->8--- >> scheme@(guile-user)> (define-syntax -> >> (syntax-rules () >> ((-> exp) >> exp) >> ((-> exp ... (op args ...)) >> (op args ... (-> exp ...))))) >> scheme@(guile-user)> (let ((t 'inner-t)) (foo t)) >> [t=global-t] inner-t >> $8 = #t >> scheme@(guile-user)> >> --8<---------------cut here---------------end--------------->8--- >> >> This macro is hygienic, and also easier to comprehend (IMO). Of course, >> it could also be implemented using syntax-case. The key is to always >> work with the syntax objects. >> >> Whenever you use 'syntax->datum' on expressions that are not purely >> literals, you will be sacrificing hygiene. > How strange. Both your and my macro gives 'global-t' when I test them, > which is the result I would expect. (Maybe I am missing something here, > but a result of 'inner-t' would seem to me to imply unhygiene.) > > However if I change my macro to manipulate syntax objects I do get > 'inner-t' > > (define-syntax --> > (lambda (x) > (syntax-case x () > [(_ exp0 exp1 ...) > (let ([reversed (reverse #'(exp0 exp1 ...))]) > (with-syntax > ([out > (let loop ([first (car reversed)] > [rest (cdr reversed)]) > (if (null? rest) > first > (syntax-case first () > [(func arg0 ...) > (append #'(func arg0 ...) > (list (loop (car rest) (cdr rest))))])))]) > #'out))]))) > > I need to think more about this and/or reproduce this later. > > This is with guile-2.2.6 by the way. > > Chris > > > > ------------------------------ > > Message: 5 > Date: Sun, 7 Jul 2019 23:25:13 +0100 > From: Chris Vine > To: Mark H Weaver > Cc: guile-user@gnu.org > Subject: Re: Threading / Pipe Macro > Message-ID: <20190707232513.5d1cff70af7f8e97380749df@gmail.com> > Content-Type: text/plain; charset=US-ASCII > > On Sun, 7 Jul 2019 21:16:13 +0100 > Chris Vine wrote: >> I need to think more about this and/or reproduce this later. >> >> This is with guile-2.2.6 by the way. > OK, I did set up the test of your macro incorrectly (the one using > syntax-rules): tested properly, as you say it produces > > [t=global-t] inner-t'. > > So does my revised syntax-case version which manipulates syntax objects. > I also agree that that is the answer that should be produced. My > version was hygienic when used in functions, but not when used as a > macro in a macro. > > Chris > > > > ------------------------------ > > Message: 6 > Date: Sun, 07 Jul 2019 18:24:32 -0400 > From: Mark H Weaver > To: Chris Vine > Cc: guile-user@gnu.org > Subject: Re: Threading / Pipe Macro > Message-ID: <87v9wd7a9w.fsf@netris.org> > Content-Type: text/plain > > Hi Chris, > > Here's a complete, unedited transcript with Guile 2.2.6: > > --8<---------------cut here---------------start------------->8--- > mhw@jojen ~$ guile > GNU Guile 2.2.6 > Copyright (C) 1995-2019 Free Software Foundation, Inc. > > Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'. > This program is free software, and you are welcome to redistribute it > under certain conditions; type `,show c' for details. > > Enter `,help' for help. > scheme@(guile-user)> (define-syntax -> > (lambda (x) > (syntax-case x () > [(k exp0 . exps) > (let* ([reversed (reverse (cons (syntax->datum #'exp0) > (syntax->datum #'exps)))] > [out (let loop ([first (car reversed)] > [rest (cdr reversed)]) > (if (null? rest) > first > (let ([func (car first)] > [args (cdr first)]) > (append `(,func ,@args) > (list (loop (car rest) (cdr rest)))))))]) > (datum->syntax #'k out))]))) > scheme@(guile-user)> (define t 'global-t) > scheme@(guile-user)> (define-syntax-rule (foo x) > (-> x (format #t "[t=~A] ~A\n" t))) > scheme@(guile-user)> (let ((t 'inner-t)) > (foo t)) > [t=global-t] global-t > $1 = #t > scheme@(guile-user)> (define-syntax -> > (syntax-rules () > ((-> exp) > exp) > ((-> exp ... (op args ...)) > (op args ... (-> exp ...))))) > scheme@(guile-user)> (let ((t 'inner-t)) > (foo t)) > [t=global-t] inner-t > $2 = #t > scheme@(guile-user)> > --8<---------------cut here---------------end--------------->8--- > > Chris Vine writes: >> How strange. Both your and my macro gives 'global-t' when I test them, > Can you show me a complete, unedited transcript that demonstrates what > you're seeing? > >> which is the result I would expect. (Maybe I am missing something here, >> but a result of 'inner-t' would seem to me to imply unhygiene.) > (foo EXPR) is supposed to print "[t=global-t] VAL", where VAL is the > result of evaluating EXPR. With this in mind, > > (let ((t 'inner-t)) > (foo t)) > > The argument to 'foo' here should refer to the lexical binding of 't', > i.e. the variable with value 'inner-t'. I'm curious what would make you > expect otherwise. > > On the other hand, the reference to 't' in the template of the 'foo' > macro should refer to the toplevel variable 't', because the template > does not appear within the 'let'. > > This is a good example of why syntax objects are needed, to distinguish > between these two references to distinct variables named 't'. When you > convert the references to datums, the distinctions are lost. > > Regards, > Mark