unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Re: Threading / Pipe Macro (Chris Vine, Mark H Weaver)
       [not found] <mailman.1204.1562538418.2685.guile-user@gnu.org>
@ 2019-07-08 21:10 ` Zelphir Kaltstahl
  2019-07-08 23:00   ` Chris Vine
  0 siblings, 1 reply; 7+ messages in thread
From: Zelphir Kaltstahl @ 2019-07-08 21:10 UTC (permalink / raw)
  To: guile-user

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 <vine35792468@gmail.com>
> To: Mark H Weaver <mhw@netris.org>
> 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 <mhw@netris.org> wrote:
>> Hi Chris,
>>
>> Chris Vine <vine35792468@gmail.com> 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 <vine35792468@gmail.com>
> To: Mark H Weaver <mhw@netris.org>
> 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 <vine35792468@gmail.com> 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 <mhw@netris.org>
> To: Chris Vine <vine35792468@gmail.com>
> 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 <vine35792468@gmail.com> 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



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Threading / Pipe Macro (Chris Vine, Mark H Weaver)
  2019-07-08 21:10 ` Threading / Pipe Macro (Chris Vine, Mark H Weaver) Zelphir Kaltstahl
@ 2019-07-08 23:00   ` Chris Vine
  2019-07-09  6:04     ` Linus Björnstam
       [not found]     ` <c95c3aa9-bd06-4a17-9392-e6256b362144@www.fastmail.com>
  0 siblings, 2 replies; 7+ messages in thread
From: Chris Vine @ 2019-07-08 23:00 UTC (permalink / raw)
  To: guile-user

On Mon, 8 Jul 2019 23:10:28 +0200t
Zelphir Kaltstahl <zelphirkaltstahl@gmail.com> wrote:
> 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

You are using the wrong macro, because the one you have chosen has been
revealed to be unhygienic.  Either use the syntax-rules one (which is
the simplest) or the revised syntax-case macro.



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Threading / Pipe Macro (Chris Vine, Mark H Weaver)
  2019-07-08 23:00   ` Chris Vine
@ 2019-07-09  6:04     ` Linus Björnstam
       [not found]     ` <c95c3aa9-bd06-4a17-9392-e6256b362144@www.fastmail.com>
  1 sibling, 0 replies; 7+ messages in thread
From: Linus Björnstam @ 2019-07-09  6:04 UTC (permalink / raw)
  To: guile-user

The comparison is also not really fair. The syntax-rules macro is doing a lot more. A simple syntax-rules macro just inserting everything in the leftmost position would loo something like

(define-syntax ->
  (syntax-rules ()
    ((-> exp) exp)
    ((-> exp (proc args ...) rest ...) (-> (proc exp args ...) rest ...)
    ((-> exp proc rest ...) (-> (proc exp) rest ...))))

I wrote this here in the mail without testing, so no guarantees that it would work. It is simple enough. I think it should work.

-- 
  Linus Björnstam

On Tue, 9 Jul 2019, at 01:02, Chris Vine wrote:
> On Mon, 8 Jul 2019 23:10:28 +0200t
> Zelphir Kaltstahl <zelphirkaltstahl@gmail.com> wrote:
> > 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
> 
> You are using the wrong macro, because the one you have chosen has been
> revealed to be unhygienic.  Either use the syntax-rules one (which is
> the simplest) or the revised syntax-case macro.
> 
>



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Threading / Pipe Macro (Chris Vine, Mark H Weaver)
       [not found]     ` <c95c3aa9-bd06-4a17-9392-e6256b362144@www.fastmail.com>
@ 2019-07-09  9:26       ` Chris Vine
  2019-07-09  9:40         ` Linus Björnstam
  0 siblings, 1 reply; 7+ messages in thread
From: Chris Vine @ 2019-07-09  9:26 UTC (permalink / raw)
  To: Linus Björnstam; +Cc: guile-user

On Tue, 09 Jul 2019 08:01:01 +0200
Linus Björnstam <linus.internet@fastmail.se> wrote:
> On Tue, 9 Jul 2019, at 01:02, Chris Vine wrote:
> > On Mon, 8 Jul 2019 23:10:28 +0200t
> > Zelphir Kaltstahl <zelphirkaltstahl@gmail.com> wrote:
> > > 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
> > 
> > You are using the wrong macro, because the one you have chosen has been
> > revealed to be unhygienic.  Either use the syntax-rules one (which is
> > the simplest) or the revised syntax-case macro.
> > 
> The comparison is also not really fair. The syntax-rules macro is doing
> a lot more. A simple syntax-rules macro just inserting everything in
> the leftmost position would loo something like
> 
> (define-syntax ->
>   (syntax-rules ()
>     ((-> exp) exp)
>     ((-> exp (proc args ...) rest ...) (-> (proc exp args ...) rest ...)
>     ((-> exp proc rest ...) (-> (proc exp) rest ...))))
> 
> I wrote this here in the mail without testing, so no guarantees that
> it would work. It is simple enough. I think it should work.

I am not sure what comparison you are referring to.  However, I was
comparing Mark Weaver's syntax-rules macro with my revised syntax-case
macro, both of which have the same end result, and suggesting that
Zelphir should not use my original syntax-case macro (the one he has
referenced in his gitlab repository).

You must be thinking of something else.



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Threading / Pipe Macro (Chris Vine, Mark H Weaver)
  2019-07-09  9:26       ` Chris Vine
@ 2019-07-09  9:40         ` Linus Björnstam
  2019-07-09  9:58           ` Chris Vine
  0 siblings, 1 reply; 7+ messages in thread
From: Linus Björnstam @ 2019-07-09  9:40 UTC (permalink / raw)
  To: Chris Vine; +Cc: guile-user

Sorry, I didn't see that macro. I was referring to my macro that Erik linked to [1], which is a syntax rules macro that also allows for <> argument placeholders:

(~> 1 1+ (/ 10 <>) iota (+ <...>)) => 10

It defaults to left insert, so

(~> 10 (/ 2)) => 5

but also (like the syntax rules macro i posted) also supports functions without parents as you can see in the first example.

That's what I get for trying to.follow the discussion in an unthreaded mobile app :)

[1]: https://bitbucket.org/bjoli/guile-threading-macros/src/default/

-- 
  Linus Björnstam

On Tue, 9 Jul 2019, at 11:26, Chris Vine wrote:
> On Tue, 09 Jul 2019 08:01:01 +0200
> Linus Björnstam <linus.internet@fastmail.se> wrote:
> > On Tue, 9 Jul 2019, at 01:02, Chris Vine wrote:
> > > On Mon, 8 Jul 2019 23:10:28 +0200t
> > > Zelphir Kaltstahl <zelphirkaltstahl@gmail.com> wrote:
> > > > 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
> > > 
> > > You are using the wrong macro, because the one you have chosen has been
> > > revealed to be unhygienic.  Either use the syntax-rules one (which is
> > > the simplest) or the revised syntax-case macro.
> > > 
> > The comparison is also not really fair. The syntax-rules macro is doing
> > a lot more. A simple syntax-rules macro just inserting everything in
> > the leftmost position would loo something like
> > 
> > (define-syntax ->
> >   (syntax-rules ()
> >     ((-> exp) exp)
> >     ((-> exp (proc args ...) rest ...) (-> (proc exp args ...) rest ...)
> >     ((-> exp proc rest ...) (-> (proc exp) rest ...))))
> > 
> > I wrote this here in the mail without testing, so no guarantees that
> > it would work. It is simple enough. I think it should work.
> 
> I am not sure what comparison you are referring to.  However, I was
> comparing Mark Weaver's syntax-rules macro with my revised syntax-case
> macro, both of which have the same end result, and suggesting that
> Zelphir should not use my original syntax-case macro (the one he has
> referenced in his gitlab repository).
> 
> You must be thinking of something else.
>



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Threading / Pipe Macro (Chris Vine, Mark H Weaver)
  2019-07-09  9:40         ` Linus Björnstam
@ 2019-07-09  9:58           ` Chris Vine
  2019-07-09 10:52             ` Linus Björnstam
  0 siblings, 1 reply; 7+ messages in thread
From: Chris Vine @ 2019-07-09  9:58 UTC (permalink / raw)
  To: Linus Björnstam; +Cc: guile-user

On Tue, 09 Jul 2019 11:40:02 +0200
Linus Björnstam <linus.internet@fastmail.se> wrote:
> Sorry, I didn't see that macro. I was referring to my macro that Erik
> linked to [1], which is a syntax rules macro that also allows for <>
> argument placeholders:
> 
> (~> 1 1+ (/ 10 <>) iota (+ <...>)) => 10
> 
> It defaults to left insert, so
> 
> (~> 10 (/ 2)) => 5
> 
> but also (like the syntax rules macro i posted) also supports functions without parents as you can see in the first example.
> 
> That's what I get for trying to.follow the discussion in an unthreaded mobile app :)
> 
> [1]: https://bitbucket.org/bjoli/guile-threading-macros/src/default/

I thought your module/library was impressive.  My little macro (as it
now turns out, defective in hygiene, although it has served me well for
some time) was something I use frequently because I like the pipeline
idiom.  It inserts to the right only, because that is what I am used
to: it sort-of imitates the reverse application of a partially applied
curried function.  I think your module is based on clojure macros,
about which I know little.



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Threading / Pipe Macro (Chris Vine, Mark H Weaver)
  2019-07-09  9:58           ` Chris Vine
@ 2019-07-09 10:52             ` Linus Björnstam
  0 siblings, 0 replies; 7+ messages in thread
From: Linus Björnstam @ 2019-07-09 10:52 UTC (permalink / raw)
  Cc: guile-user

My macro is was based on the  |> pipeline from f# and on the cut macro. I then changed the syntax after seing the pipeline macro in rackjure, which is based on clojure's macros. In the beginning I wanted to do destructuring as well to allow to destructure lists and passed it as numbered arguments and such. I had a syntax-case version that did all that, but I never used that, so I wrote a syntax-rules macro instead so that I could bring it with me if I ever switched schemes (from chez). I switched to chicken and then guile and brought the macro with me. 

If you want to use it, I also provide a right-inserting macro as well: (~>> ...). The license is more or less "just don't remove this copyright notice from the source", so feel free :D

-- 
  Linus Björnstam

On Tue, 9 Jul 2019, at 11:58, Chris Vine wrote:
> On Tue, 09 Jul 2019 11:40:02 +0200
> Linus Björnstam <linus.internet@fastmail.se> wrote:
> > Sorry, I didn't see that macro. I was referring to my macro that Erik
> > linked to [1], which is a syntax rules macro that also allows for <>
> > argument placeholders:
> > 
> > (~> 1 1+ (/ 10 <>) iota (+ <...>)) => 10
> > 
> > It defaults to left insert, so
> > 
> > (~> 10 (/ 2)) => 5
> > 
> > but also (like the syntax rules macro i posted) also supports functions without parents as you can see in the first example.
> > 
> > That's what I get for trying to.follow the discussion in an unthreaded mobile app :)
> > 
> > [1]: https://bitbucket.org/bjoli/guile-threading-macros/src/default/
> 
> I thought your module/library was impressive.  My little macro (as it
> now turns out, defective in hygiene, although it has served me well for
> some time) was something I use frequently because I like the pipeline
> idiom.  It inserts to the right only, because that is what I am used
> to: it sort-of imitates the reverse application of a partially applied
> curried function.  I think your module is based on clojure macros,
> about which I know little.
>



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-07-09 10:52 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <mailman.1204.1562538418.2685.guile-user@gnu.org>
2019-07-08 21:10 ` Threading / Pipe Macro (Chris Vine, Mark H Weaver) Zelphir Kaltstahl
2019-07-08 23:00   ` Chris Vine
2019-07-09  6:04     ` Linus Björnstam
     [not found]     ` <c95c3aa9-bd06-4a17-9392-e6256b362144@www.fastmail.com>
2019-07-09  9:26       ` Chris Vine
2019-07-09  9:40         ` Linus Björnstam
2019-07-09  9:58           ` Chris Vine
2019-07-09 10:52             ` Linus Björnstam

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).