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 19:32:59 +0100

On Sun, 7 Jul 2019 12:42:03 +0200
Zelphir Kaltstahl <address@hidden> wrote:
> Hi Guile Users!
> 
> I recently looked at some online course about Elixir and saw an elegant
> use of pipes (the operator `|>` in Elixir). Then I remembered, that in
> Racket there are so called threading macros, which seem to accomplish
> the same:
> 
> https://github.com/lexi-lambda/threading/blob/master/threading-lib/threading/main.rkt
> 
> I also searched around for tutorials or explanations on how to write
> these macros. Sometimes I found excellent documenation in the Chicken
> Scheme wiki, so I checked there:
> 
> https://wiki.call-cc.org/eggref/5/pipes
> 
> However, I would like to use a threading macro or pipes in Guile. I am
> seeing these options:
> 
> (1) I could start trying to translate the Racket version to Guile, maybe
> it would work easily, maybe it uses Racket specific macro stuff, I don't
> know. However, I am not sure I would learn how the macros actually work.
> Maybe I would.
> 
> (2) I could start from zero and try to implement the pipes I saw in the
> online course about Elixir.
> 
> (3) Maybe something already exists in Guile, that I am unaware of and
> could not find through searching. Maybe there are even more names for pipes.
> 
> So my questions are:
> 
> (1) Does something already exist?
> 
> (2) Would translating the Racket version be an easy thing to do, or is
> there something in there, that cannot so easily be achieved with
> syntax-parse, syntax-case and the likes? (For someone who rarely touches
> macros and does not have a lot experience writing them.)

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.

The syntax of the -> macro is like this:

(-> (+ 3 4 5)
    (- 18)
    (format #t "~A\n"))

The first expression in the '->' block is evaluated, and its value is
passed as the last (or only) argument of the following expression, and
so on.  Each expression after the first one must be a function application
(the first one can be either a function application or a value).  The
applied function comprising each expression after the first one appears to
be curried, but in fact there is no currying, nor any runtime overhead at
all.  The above example code is expanded into the following scheme form:

(format #t "~A\n" (- 18 (+ 3 4 5)))

You can use it with a monadic bind (or applicative mapping) if you want so
as to behave in a similar way to the >>= operator, but such things have
little use in scheme in my opinion.  (I can however provide a simple
example of that if you think it would be interesting.)

Chris



reply via email to

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