Macros for pipelining

Q&A's, tips, howto's
Locked
William James
Posts: 58
Joined: Sat Jun 10, 2006 5:34 am

Macros for pipelining

Post by William James »

Here are two "threading" or pipelining macros, similar to those in Clojure:

Code: Select all

(context '->>)
(define-macro (->>:->> E form)
  (if (empty? (args))
    (if (list? form)
      (eval (push E form -1))
      (eval (list form E)))
    (eval (cons '->> (cons (list '->> E form) (args))))))

(context '->)
(define-macro (->:-> E form)
  (if (empty? (args))
    (if (list? form)
      (eval (cons (first form) (cons E (rest form))))
      (eval (list form E)))
    (eval (cons '-> (cons (list '-> E form) (args))))))

(context MAIN)
Consider this sequential application of three functions:

Code: Select all

: (exp (sqrt (abs -3)))
5.652233674
Using one of the macros, the functions appear in the same order that they are applied and fewer parentheses are needed:

Code: Select all

: (-> -3 abs sqrt exp)
5.652233674
The -> macro feeds the item as the first argument to the function:

Code: Select all

: (-> 8 (div 4))
2
The ->> macro feeds the item as the last argument to the function:

Code: Select all

: (->> 8 (div 4))
0.5
Let's extract the values from an association list, select only those that are less than 50, and add them up.

Code: Select all

(setq alist '((a 29)(b 25)(c 21)(d 64)))
: (->> alist (map last) (filter (curry > 50)) (apply +))
75

johu
Posts: 143
Joined: Mon Feb 08, 2010 8:47 am

Re: Macros for pipelining

Post by johu »

Fine!
Maybe, eval might not be needed.

Code: Select all

(context 'MAIN:->>)
(define-macro (->>:->> E form)
  (letex (_func
            (if $args (cons '->> (cons (list '->> E form) $args))
                (list? form) (push E form -1)
                (list form E)))
  _func))
(context 'MAIN:->)
(define-macro (->:-> E form)
  (letex (_func
            (if $args (cons '-> (cons (list '-> E form) $args))
                (list? form) (push E form 1)
                (list form E)))
  _func))
(context MAIN)

William James
Posts: 58
Joined: Sat Jun 10, 2006 5:34 am

Re: Macros for pipelining

Post by William James »

Interesting approach; I'm not very familiar with letex.

Since we are putting each macro in its own context, I think you can safely use func instead of _func.

I like "(if $args" instead of "(if (empty? (args))"; shorter and cleaner.

You gave me the idea of just using one eval:

Code: Select all

(context '->>)
(define-macro (->>:->> E form)
  (eval
    (if $args
      (cons '->> (cons (list '->> E form) (args)))
      (if (list? form)
        (push E form -1)
        (list form E)))))

(context '->)
(define-macro (->:-> E form)
  (eval
    (if $args
      (cons '-> (cons (list '-> E form) (args)))
      (if (list? form)
        (cons (first form) (cons E (rest form)))
        (list form E)))))
(context MAIN)
I find that using these macros is fun. Clojure comes with them, and something similar can be easily defined in OCaml.

Locked