Macros for pipelining

Started by William James, April 07, 2012, 09:35:37 AM

Previous topic - Next topic

William James

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

(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:


: (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:

: (-> -3 abs sqrt exp)
5.652233674

The -> macro feeds the item as the first argument to the function:

: (-> 8 (div 4))
2

The ->> macro feeds the item as the last argument to the function:

: (->> 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.


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

johu

#1
Fine!

Maybe, eval might not be needed.


(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

#2
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:



(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.