[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Threading / Pipe Macro
From: |
Mark H Weaver |
Subject: |
Re: Threading / Pipe Macro |
Date: |
Sun, 07 Jul 2019 15:30:36 -0400 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux) |
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.
Regards,
Mark
Re: Threading / Pipe Macro, Erik Edrosa, 2019/07/07