loopy

Homepage: https://github.com/okamsn/loopy

Author: Earl Hyatt

Updated:

Summary

A looping macro

Commentary

The `loopy' macro is used to generate code for a loop, similar to `cl-loop'.
Unlike `cl-loop', `loopy' uses symbolic expressions instead of "clauses".

A simple usage of `cl-loop':

    (cl-loop for i from 1 to 10
             if (cl-evenp i) collect i into evens
             else collect i into odds
             end ; This `end' keyword is optional here.
             finally return (list odds evens))

How it could be done using `loopy':

    (loopy (numbers i 1 10)
           (if (cl-evenp i)
               (collect evens i)
             (collect odds i))
           (finally-return odds evens))

`loopy' supports destructuring for iteration commands like `list' and
accumulation commands like `sum' or `collect'.

    ;; Summing the nth elements of arrays:
    ;; => (8 10 12 14 16 18)
    (loopy (list (list-elem1 list-elem2)
                 '(([1 2 3] [4 5 6])
                   ([7 8 9] [10 11 12])))
           (sum [sum1 sum2 sum3] list-elem1)
           (sum [sum4 sum5 sum6] list-elem2)
           (finally-return sum1 sum2 sum3 sum4 sum5 sum6))

    ;; Or, more simply:
    ;; => (8 10 12 14 16 18)
    (loopy (list list-elem '(([1 2 3] [4 5 6])
                             ([7 8 9] [10 11 12])))
           (sum ([sum1 sum2 sum3] [sum4 sum5 sum6])
                list-elem)
           (finally-return sum1 sum2 sum3 sum4 sum5 sum6))

    ;; Separate the elements of sub-list:
    ;; => ((1 3) (2 4))
    (loopy (list i '((1 2) (3 4)))
           (collect (elem1 elem2) i)
           (finally-return elem1 elem2))

The `loopy' macro is configurable and extensible.  In addition to writing
one's own "loop commands" (such as `list' in the example below), by using
"flags", one can choose whether to instead use `pcase-let', `seq-let', or
even the Dash library for destructuring.

    ;; Use `pcase' to destructure array elements:
    ;; => ((1 2 3 4) (10 12 14) (11 13 15))
    (loopy (flag pcase)
           (array (or `(,car . ,cdr) digit)
                  [1 (10 . 11) 2 (12 . 13) 3 4 (14 . 15)])
           (if digit
               (collect digits digit)
             (collect cars car)
             (collect cdrs cdr))
           (finally-return digits cars cdrs))

    ;; Using the default destructuring:
    ;; => ((1 2 3 4) (10 12 14) (11 13 15))
    (loopy (array elem [1 (10 . 11) 2 (12 . 13) 3 4 (14 . 15)])
           (if (numberp elem)
               (collect digits elem)
             (collect (cars . cdrs) elem))
           (finally-return digits cars cdrs))

Variables like `cars', `cdrs', and `digits' in the example above are
automatically `let'-bound so as to not affect code outside of the loop.

`loopy' has arguments for binding (or not binding) variables, executing code
before/after the loop, executing code only if the loop completes, and for
setting the macro's return value (default `nil').  This is in addition to the
looping features themselves.

All of this makes `loopy' a useful and convenient choice for looping and
iteration.

That being said, Loopy is not yet feature complete.  Please request features
or report problems in this project’s issues tracker
().  While most cases are covered,
full feature parity with some of the more niche uses of `cl-loop' is still
being worked on.

For more information, including the full list of loop commands and how to
extend the macro, see this package's comprehensive Info documentation under
the Info node `(loopy)'.

Dependencies