guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Threading / Pipe Macro


From: Chris Vine
Subject: Re: Threading / Pipe Macro
Date: Sun, 7 Jul 2019 21:16:13 +0100

On Sun, 07 Jul 2019 15:30:36 -0400
Mark H Weaver <address@hidden> wrote:
> Hi Chris,
> 
> Chris Vine <address@hidden> 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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]