Search code examples
lispmacroscommon-lispchainingpiping

Lisp chaining functions macro


Is there a ready made lisp macro that allows chaining (piping) of functions? I couldn't find one. I'll try to explain what I mean with this example.

Instead of using let* with lots of unused intermediate variables like this:

(let*
  ((var1 (f1 x y))
   (var2 (f2 x var1))
   (var3 (f1 var2 z)))
 var3)

I would like to have it written like this:

(->
  (f1 x y)
  (f2 x _)
  (f1 _ z))

where, obviously _ will be return value from previous expression. A plus is if would be possible to use _1, _2, ... to reference previously returned values.

This is the idea, exact syntax is not that important.

I know this is not that hard to write, but seems so useful that it has to be written already.


Solution

  • Something like this?

    (defun chain-expander (forms)
      (cond ((null (cdr forms)) (car forms))
        (t `(let ((it ,(car forms)))
              ,(chain-expander (cdr forms))))))
    
    (defun chain-counted-expander (forms counter)
      (cond ((null (cdr forms)) (car forms))
        (t (let* ((name (format nil "_~d" counter))
              (anaphora (or (find-symbol name) (intern name))))
             `(let ((,anaphora ,(car forms)))
            ,(chain-counted-expander (cdr forms) (1+ counter)))))))
    
    (defmacro chain (&body forms)
      (chain-expander forms))
    

    If you'd prefer something where _1, _2, and so on is usable, simply replace the call to CHAIN-EXPANDER with a call to CHAIN-COUNTED-EXPANDER (with your preferred first number, I'd suggest either 0 or 1). Note that it explicitly only caters to using _N as a reference, but changing it so that it also binds _ for each subsequent level is not very hard.