# -*- lexical-binding: t; -*-
# Conventions for this document:
#
# 1. Because the exporter doesn't support custom Texinfo indices,
#    - Special macro arguments go in pindex
#    - Loop commands go in tindex
#
# 2. "Loopy" is the library, "~loopy~" is the macro, and "=loopy=" is the loop
#    command.  Now that we have ~loopy~ and ~loopy-iter~, looping constructs
#    should generally be described as part of "Loopy", not a particular macro.

#+title: Loopy: A Looping and Iteration Macro
#+author: Earl Hyatt
#+export_file_name: loopy

# Make sure to export all headings as such.  Otherwise, some links to
# sub-headings won’t work.
#+options: H:6
# Some parsers require this option to export footnotes.
#+options: f:t

# Example settings
#+PROPERTY: header-args:emacs-lisp :lexical t

# Texinfo settings.
#+TEXINFO_FILENAME: loopy.info
#+TEXINFO_DIR_CATEGORY: Emacs
#+TEXINFO_DIR_TITLE: Loopy: (loopy)
#+TEXINFO_DIR_DESC: A better looping and iteration macro.

#+MACRO: dfn @@texinfo:@dfn{$1}@@
#+MACRO: kbd (eval (org-texinfo-kbd-macro $1))
#+MACRO: file @@texinfo:@file{$1}@@
#+MACRO: var @@texinfo:@var{$1}@@

#+begin_export html
NOTE: This file is meant for viewing in Org mode or for exporting to Texinfo.
Features might not work in other formats (for example, Info links in HTML).
#+end_export

~loopy~ is a macro meant for iterating and looping.  It is similar in usage to
~cl-loop~ ([[info:cl#Loop Facility]]) but uses parenthesized expressions rather than
keyword clauses.

For most cases, ~loopy~ is a featureful replacement for ~cl-loop~ and
complementary to Emacs's built-in looping and mapping features (such as the
libraries =seq= ([[info:elisp#Sequence Functions]]) and =cl-lib= ([[info:cl]])).


-----

# This auto-generated by toc-org.
* Table of Contents                                                :TOC:noexport:
- [[#introduction][Introduction]]
- [[#basic-concepts][Basic Concepts]]
- [[#special-macro-arguments][Special Macro Arguments]]
- [[#loop-commands][Loop Commands]]
  - [[#basic-destructuring][Basic Destructuring]]
  - [[#generic-evaluation][Generic Evaluation]]
  - [[#iteration][Iteration]]
    - [[#generic-iteration][Generic Iteration]]
    - [[#numeric-iteration][Numeric Iteration]]
    - [[#sequence-iteration][Sequence Iteration]]
    - [[#sequence-index-iteration][Sequence Index Iteration]]
    - [[#sequence-reference-iteration][Sequence Reference Iteration]]
  - [[#accumulation][Accumulation]]
    - [[#common-properties-and-arguments-of-accumulation-commands][Common Properties and Arguments of Accumulation Commands]]
    - [[#generic-accumulation][Generic Accumulation]]
    - [[#numeric-accumulation][Numeric Accumulation]]
    - [[#sequence-accumulation][Sequence Accumulation]]
    - [[#other-accumulation-commands][Other Accumulation Commands]]
    -  [[#optimizing-accumulations][Optimizing Accumulations]]
  - [[#checking-conditions][Checking Conditions]]
  - [[#control-flow][Control Flow]]
    - [[#conditional-execution][Conditional Execution]]
    - [[#skipping-cycles][Skipping Cycles]]
    - [[#early-exit][Early Exit]]
  - [[#sub-loops][Sub-Loops]]
- [[#destructuring-macros][Destructuring Macros]]
- [[#the-loopy-iter-macro][The ~loopy-iter~ Macro]]
  - [[#default-bare-names-in-loopy-iter][Default Bare Names in ~loopy-iter~]]
- [[#customizing-macro-behavior][Customizing Macro Behavior]]
  - [[#using-flags][Using Flags]]
    - [[#destructuring-flags][Destructuring Flags]]
    - [[#the-no-loop-flag][The =no-loop= Flag]]
  - [[#custom-aliases][Custom Aliases]]
  - [[#custom-commands][Custom Commands]]
    - [[#background-info][Background Info]]
    - [[#hello-world][Hello World]]
    - [[#an-always-command][An ~always~ Command]]
    - [[#custom-commands-in-the-loopy-iter-macro][Custom commands in the ~loopy-iter~ macro]]
    - [[#finding-more-examples][Finding More Examples]]
  - [[#locally-overriding-behavior][Locally Overriding Behavior]]
- [[#comparing-to-cl-loop][Comparing to ~cl-loop~]]
- [[#translating-to-and-from-cl-loop][Translating to and from ~cl-loop~]]
  - [[#for-clauses][For Clauses]]
  - [[#iteration-clauses][Iteration Clauses]]
  - [[#accumulation-clauses][Accumulation Clauses]]
  - [[#other-clauses][Other Clauses]]
- [[#index-of-concepts][Index of Concepts]]
- [[#index-of-variables][Index of Variables]]
- [[#index-of-functions-and-macros][Index of Functions and Macros]]
- [[#index-of-special-macro-arguments][Index of Special Macro Arguments]]
- [[#index-of-loop-commands][Index of Loop Commands]]
- [[#footnotes][Footnotes]]

* Introduction
:PROPERTIES:
:DESCRIPTION: A short overview of features.
:END:

Loopy is a library for looping and iteration, with supplemental features for
destructuring.  Upfront, the features provided are summarized below.  They are
described thoroughly later in this document.

#+findex: loopy
- ~loopy~ :: A macro similar to ~cl-loop~.  Unlike ~cl-loop~, ~loopy~ uses
  parenthetical forms instead of keyword "clauses".

  #+begin_src emacs-lisp
    ;; 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 :from 1 :to 10)
           (if (cl-evenp i)
               (collect evens i)
             (collect odds i))
           (finally-return odds evens))
  #+end_src

#+findex: loopy-iter
- ~loopy-iter~ :: A macro similar to Common Lisp's Iterate macro ([[#loopy-iter]]).
  Unlike Common Lisp's ~loop~, the ~iterate~ macro allows one to embed its
  looping constructs inside of arbitrary code.  To be clear, ~loopy-iter~ is
  _not_ a port of ~iterate~ to Emacs Lisp.

  #+begin_src emacs-lisp
    ;; => ((1 2 3) (-3 -2 -1) (0))
    (loopy-iter (numbering i -3 3)
                ;; Normal `pcase' macro, not a `loopy' feature:
                (pcase i
                  ;; `collecting' is a `loopy' feature:
                  ((pred cl-plusp)  (collecting positives i))
                  ((pred cl-minusp) (collecting negatives i))
                  (_                (collecting other i)))
                (finally-return positives negatives other))
  #+end_src

#+findex: loopy-let
#+findex: loopy-setq
#+findex: loopy-lambda
#+findex: loopy-ref
- ~loopy-let*~, ~loopy-setq~, ~loopy-lambda~, and ~loopy-ref~ :: Destructuring
  macros that can be used outside of ~loopy~ and ~loopy-iter~
  ([[#basic-destructuring]]).  For convenience, Loopy provides its own form of
  destructuring, which is similar to, though more featureful than, that provided
  by =cl-lib=.

  #+begin_src emacs-lisp
    ;; => (1 2 3 (:k1 4) 4)
    (loopy-let* (((a b c &rest d &key k1) '(1 2 3 :k1 4)))
      (list a b c d k1))

    ;; => ((7 2 8) [9 2 10])
    (let ((my-list (list 1 2 3))
          (my-vector  (vector 1 2 3)))
      (loopy-ref (((a _ c) my-list)
                  ([d _ e] my-vector))
        (setf a 7
              c 8
              d 9
              e 10)
        (list my-list my-vector)))
  #+end_src

- A ~pcase~ pattern :: Loopy provides a ~pcase~ pattern, called =loopy=, which
  can be used in macros like ~pcase-lambda~ and ~pcase-let~.  Some of the
  destructuring macros provided by Loopy are implemented using this ~pcase~
  macro.

  #+begin_src emacs-lisp
    ;; => (1 2 3 (:k1 4) 4)
    (pcase-let (((loopy (a b c &rest d &key k1))
                 '(1 2 3 :k1 4)))
      (list a b c d k1))
  #+end_src


Some other things to note are:

- ~loopy~ (and so ~loopy-iter~) supports destructuring for both iteration and
  accumulation commands.

  #+begin_src emacs-lisp
    ;; 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))

    ;; 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))
  #+end_src

- The looping macros are configurable and extensible.  One can create one's own
  loop commands and command aliases.  "Flags" can be used to configure the
  looping macros' behavior, such as by using an alternative destructuring system
  like =pcase= or the Dash library.

  #+begin_src emacs-lisp
    ;; 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))
  #+end_src

- Compared to ~cl-loop~, more constructs are provided for how loops are
  completed and how values are returned.  For example, the =leave= command will
  exit the loop without changing what would be returned.  This is a more generic
  form of the commands =while= and =until=, though those are also provided.
  =after-do= (a.k.a. =else-do=) is a construct that runs Lisp code only when the
  loop completes successfully, similar to Python's ~else~ statement after ~for~
  and ~while~ loops.

  #+begin_src emacs-lisp
    ;; => (1 3 5)
    (loopy (numbers i :from 1 :to 10)
           (cond ((cl-evenp i) (skip))
                 ((> i 5)      (leave)))
           (collect i))

    ;; => (t nil)
    (loopy (with (always-run)
                 (run-when-complete))
           (numbers i :from 1 :to 10)
           (while (< i 4))
           (after-do   (setq run-when-complete t))
           (finally-do (setq always-run t))
           (finally-return always-run run-when-complete))
  #+end_src

- One can declare which accumulations variables should be optimized (and so
  treated as user-inaccessible during the loop).  This can make accumulation
  into multiple named variables much faster.  As needed, one can make Loopy
  optimize for at-start accumulations or at-end accumulations.

  #+begin_src emacs-lisp
    ;; Expands into the efficient `push'-`nreverse' idiom, not
    ;; the `nonc'-`list' idiom that would be used by `cl-loop'.
    ;; => ((1 3) (2 4))
    (loopy (accum-opt cars cdrs)
           (array elem [(1 . 2) (3 . 4)])
           (collect (cars . cdrs) elem)
           (finally-return cars cdrs))

    ;; Prioritizes collecting at the start of `my-var'.
    ;; => (5 3 1 4 6)
    (loopy (accum-opt (my-var start))
           (array (car . cdr) [(1 . 2) (3 . 4) (5 . 6)])
           (collect my-var car :at start)
           (when (> cdr 2)
             (collect my-var cdr :at end))
           (finally-return my-var))
  #+end_src


All that being said, Loopy is not yet feature complete.  Please request features
or report problems in this project’s [[https://codeberg.org/okamsn/loopy/issues][issues tracker]].

* Basic Concepts
:PROPERTIES:
:CUSTOM_ID: basic-concepts
:DESCRIPTION: Basic information about `loopy' and its loops.
:END:

Except for an optional loop name, all arguments of the ~loopy~ macro are
parenthesized expressions.  These expressions can, for example, assign variables
local to the loop, add code that runs before/after the loop, and/or set the
ultimate return value of the macro.

For convenience and clarity, expressions that generate code in the loop body are
called {{{dfn(loop commands)}}} ([[#loop-commands][Loop Commands]]).  Expressions that generate code
around the loop are called {{{dfn(special macro arguments)}}} or just
{{{dfn(macro arguments)}}} as opposed to {{{dfn(loop commands)}}}
([[#macro-arguments][Special Macro Arguments]]).

Loop commands are the main building blocks of the ~loopy~ macro, such as the
command =list= in the expression =(list i '(1 2 3))=.  A command inserts code
into the loop body, but can also perform additional setup like initializing
variables.  Many commands set a condition for ending the loop.  In the case of
=list= in the above expression, the command iterates through the elements of a
list, binding the variable ~i~ to each element.  After iterating through all
elements, the loop is forced to end.

In general, a loop ends when any looping condition required by a loop command
evaluates to ~nil~.  If no conditions are needed, then the loop runs infinitely
until an early-exit command is reached ([[#exiting-the-loop-early][Exiting the Loop Early]]).

The default return value of the loop is ~nil~.  Other return values must be
stated explicitly, as in one of the early-exit commands or as part of the
=finally-return= macro argument, or come from accumulating loop commands using
an implied accumulation variable ([[#accumulation-commands][Accumulation Commands]]).

The ~loopy~ macro is configurable.  One can add custom commands ([[#adding-custom-commands][Custom
Commands]]), add custom command aliases ([[#custom-aliases][Custom Aliases]]), and specify macro
options for a particular loop ([[#flags][Using Flags]]).  Each of these features is
explained in detail later in this document.

* Special Macro Arguments
:PROPERTIES:
:CUSTOM_ID: macro-arguments
:DESCRIPTION: Creating the environment of the loop.
:END:

#+cindex: special macro argument
There are only a few special macro arguments.  If a macro argument does not
match one of these special few, ~loopy~ will attempt to interpret it as a loop
command, and will signal an error if that fails.

These special macro arguments are always processed before loop commands,
regardless of the order of the arguments passed to ~loopy~.

For clarity, some special macro arguments have multiple names. For example, the
special macro argument =with= can also be written as =init=, because the
argument =with= is used to initialized variables.

#+pindex: named
- =named= or just a symbol :: Name the loop.  This also names the ~cl-block~
  which contains the loop.  This can be of the form =(named NAME)= or just
  =NAME=.

  #+begin_src emacs-lisp
    ;; => 3
    (loopy outer
           (array i [(1 2) (3 4) (5 6)])
           (loopy (list j i)
                  (when (> j 2)
                    (return-from outer j))))

    ;; => 3
    (loopy (named outer)
           (array i [(1 2) (3 4) (5 6)])
           (loopy (list j i)
                  (when (> j 2)
                    (return-from outer j))))
  #+end_src

#+pindex: with
#+pindex: let*
#+pindex: init
- =with=, =let*=, =init= :: Declare variables before the loop, in order.  This
  can also be used to initialize variables referenced by loop commands.  =with=
  can use destructuring ([[#basic-destructuring]]).

  #+begin_src emacs-lisp
    ;; => (4 5 6)
    (loopy
     (with (a 1)          ; Set `a' to 1.
           (b (1+ a)))    ; Set `b' to 1+1=2.
     (list i '(1 2 3))    ; Bind `i' to elements of the list.
     (collect (+ i a b))) ; Collect sum of `a', `b', and each `i' into a list.
  #+end_src

#+pindex: without
#+pindex: no-with
#+pindex: no-init
- =without=, =no-with=, =no-init= :: Variables that ~loopy~ should not try to
  initialize.  ~loopy~ tries to initialize all of the variables that it uses
  in a ~let~-like form, but that isn’t always desired.

  #+begin_src emacs-lisp
    ;; Without `without', `loopy' would try to initialize `a' to nil, which would
    ;; shadow the variable `a' bound by the `let' form.

    ;; => (5 4 3 2 1)
    (let ((a 5))
      (loopy (without a)        ; Don't initialize `a'.
             (until (zerop a))  ; Leave loop when `a' equals 0.
             (collect a)        ; Collect the value of `a' into a list.
             (set a (1- a))))   ; Set `a' to the value of `(1- a)'.
  #+end_src

#+pindex: before
#+pindex: before-do
#+pindex: initially-do
#+pindex: initially
- =before-do=, =before=, =initially-do=, =initially= :: Run Lisp expressions
  before the loop starts, after variables are initialized.

  #+begin_src emacs-lisp
    ;; => (6 7 8)
    (loopy (with (a 1) (b 2))      ; Set `a' to 1 and `b' to 2.
           (before-do (cl-incf a)  ; Add 1 to `a'.
                      (cl-incf b)) ; Add 1 to `b'.
           (list i '(1 2 3))       ; Set `i' to each element in the list.
           (collect (+ i a b)))    ; Collect each sum into a list.
  #+end_src

#+pindex: after-do
#+pindex: after
#+pindex: else-do
#+pindex: else
- =after-do=, =after=, =else-do=, =else= :: Run Lisp expressions after the loop
  successfully completes.  This is similar to Python’s ~else~ statement
  following a ~for~ or ~while~ loop.  Unlike ~progn~, the return values of the
  expressions _do not_ affect the return value of the macro.

  #+begin_src emacs-lisp
    ;; Messages that an odd number was found:
    ;; => t
    (loopy (list i '(2 4 5 8))
           (when (cl-oddp i)
             (do (message "Odd number found."))
             (return t))
           (after-do (message "No odd number found.")))
  #+end_src

#+pindex: finally-do
#+pindex: finally
- =finally-do=, =finally= :: Run Lisp expressions after the loop exits, always.
  Unlike ~progn~, the return values of the expressions _do not_ affect the
  return value of the macro.

  #+begin_src emacs-lisp
    ;; => (nil finally)
    (let (a b)
      (loopy (list i '(1 2 3 4 5 6))
             (when (> i 3) (leave))
             (after-do (setq a 'after))
             (finally-do (setq b 'finally)))
      (list a b))

    ;; => nil
    (loopy (leave)
           ;; Doesn't affect return value:
           (finally-do 999))
  #+end_src

  When used with =finally-return=, code in =finally-do= is run before code in
  =finally-return=.

  #+begin_src emacs-lisp
    ;; => (-1 0 1 2 3)
    (loopy (list i '(1 2 3))
           (collect collection i)
           (finally-do (push 0 collection))
           (finally-return (cons -1 collection)))
  #+end_src

  =finally-do= has access to the variables created by the loop after the loop
  finishes, including accumulation variables that you might wish to use as a
  return value ([[#accumulation-commands]]).  For consistency with explicit
  accumulation variables ([[#optimized-accums]]), so long as the loop completes
  successfully, =finally-do= can modify ~loopy-result~ even when it is used
  as an implied accumulation variable.

  #+begin_src emacs-lisp
    ;; => (0 1 2 3)
    (loopy (list i '(1 2 3))
           (collect i)
           (finally-do (push 0 loopy-result)))
  #+end_src

#+pindex: finally-return
- =finally-return= :: Return a value, regardless of how the loop completes.
  These arguments override any explicit return values given in commands like
  =return= and =return-from=, as well as any implicit return values that can
  be created by accumulation commands.

  Specifying multiple values is the same as returning a list of those values.

  #+begin_src emacs-lisp
    ;; => 999
    (loopy (return 1)
           (finally-return 999))

    ;; => (1 2)
    (loopy (leave) ; Leave to avoid infinite loop.
           (finally-return 1 2))
  #+end_src

  When used with =finally-do=, code in =finally-return= is run after code in
  =finally-do=.

  #+begin_src emacs-lisp
    ;; => (-1 0 1 2 3)
    (loopy (list i '(1 2 3))
           (collect collection i)
           (finally-do (push 0 collection))
           (finally-return (cons -1 collection)))
  #+end_src


#+pindex: finally-protect
#+pindex: finally-protected
- =finally-protect=, =finally-protected= :: Wrap the loop in ~unwind-protect~
  (not to be confused with ~condition-case~).  The arguments to this special
  macro argument (which are Lisp expressions) can access the variables used by
  the loop.

  Signaling an error will prevent the loop from returning a value.  This
  special macro argument does not prevent that error from being signaled, and
  is only meant to help avoid lingering effects that might arise from
  unplanned stops of the loop's execution.

  #+begin_src emacs-lisp
    ;; Prints out the following, then continues signalling the error:
    ;;
    ;; Example var is: 1
    ;; Last used element in list is: 4
    ;; Then current value of ‘my-collection’: (1 2 3 4)
    (loopy (with (example-var 1))
           (list i '(1 2 3 4 5))
           (collect my-collection i)
           (when (> i 3)
             (do (signal 'some-error (list i))))
           (finally-protect
            (message "Example var is: %d" example-var)
            (message "Last used element in list is: %s" i)
            (message "Then current value of `my-collection': %s"
                     my-collection)))
  #+end_src

#+pindex: flag
#+pindex: flags
- =flag=, =flags= :: Options that change the behavior of ~loopy~ ([[#flags]]).
  For example, one can opt to use a different destructuring system, such as
  what is provided by the Dash library.  See that linked section for more
  information.

  #+begin_src emacs-lisp
    ;; Use Dash for destructuring:
    ;;
    ;; (((1 2) (3 4)) (1 3) (2 4))
    (require 'loopy-dash)
    (loopy (flag dash)
           (list (whole &as a b) '((1 2) (3 4)))
           (collect wholes whole)
           (collect as a)
           (collect bs b)
           (finally-return wholes as bs))

    ;; Use Seq for destructuring:
    ;;
    ;; => (1 [2 3])
    (loopy (flag seq)
           (with ([a &rest b] [1 2 3]))
           (return a b))
  #+end_src

#+pindex: accum-opt
#+pindex: opt-accum
- =accum-opt=, =opt-accum= :: Accumulation variables whose use should be
  optimized ([[#optimized-accums]]).  Implicit accumulation variables are always
  optimized, but explicit variables are unoptimized by default
  ([[#accumulation-commands]]).  This special macro argument allows optimizing named
  variables (with restrictions), which is useful when using more than one
  accumulation variable.  This is especially important for destructuring
  accumulations.

  #+begin_src emacs-lisp
    ;; Multiple accumulation variables in `cl-loop':
    ;; => ((2 4) (1 3))
    (cl-loop for i in '(1 2 3 4)
             if (cl-evenp i) collect i into evens
             else collect i into odds
             finally return (list evens odds))

    ;; Faster code than above `cl-loop' (try `pp-macroexpand-last-sexp'):
    ;; => ((2 4) (1 3))
    (loopy (accum-opt evens odds)
           (list i '(1 2 3 4))
           (if (cl-evenp i)
               (collect evens i)
             (collect odds i))
           (finally-return evens odds))
  #+end_src


#+pindex: wrap
- =wrap= :: A list of forms in which to wrap the loop itself (that is, not
  =before-do=, =after-do=, or anything else).  Each form can be either a list
  or a symbol.  If a list, the loop is inserted into the end of the list.  If
  a symbol, it is first converted to a list of 1 element before inserting the
  loop at the end of the list.  This special macro argument is similar in use
  to the Emacs Lisp macro ~thread-last~, except that forms listed first are
  applied last, as in normal Lisp code.

  The main difference between using this macro argument instead of just
  writing the function calls normally is that these forms can access variables
  initialized by the macro and that they occur after the code in =before-do=
  is run.

  #+begin_src emacs-lisp
    (loopy (wrap (let ((a 1)))
                 save-match-data)
           ...)

    ;; Similar to
    (let ((a 1))
      (save-match-data
        (loopy ...)))

    ;; => 6
    (loopy (with (a 1))
           (before-do (cl-incf a 2))
           (wrap (progn (setq a (* 2 a))))
           (return a))
  #+end_src


As stated above, all other expressions will be considered loop commands
([[#loop-commands][Loop Commands]]).

#+ATTR_TEXINFO: :tag Note
#+BEGIN_QUOTE
For convenience, the ~while~-loop that ~loopy~ creates is wrapped by a
~cl-block~.  Naming the loop names this block, which is created /after/
initializing variables.

The two special macro arguments =before-do= and =after-do= (and their aliases)
also occur within this ~cl-block~, before and after the loop, respectively.
This has 2 consequences:

1. Using ~cl-return~ in =before-do= will prevent the both loop and =after-do=
   code from running.

2. Using ~cl-return~ or an early exit command ([[#exiting-the-loop-early][Early Exit]]) in the loop will
   prevent the =after-do= code from running.  For this reason, =after-do= is
   run if and only if the loop completes successfully, hence the alias
   =else-do= and the similarity to Python's ~else~ statement when used with
   loops.

These three sections (=before-do=, =after-do=, and the ~while~-loop itself)
are the only structures that occur within the ~cl-block~.  Using ~cl-return~
in =before-do=, for example, will not stop code in =finally-do= from running
or values listed in =finally-return= from being returned.
#+END_QUOTE

* Loop Commands
:PROPERTIES:
:CUSTOM_ID: loop-commands
:DESCRIPTION: The main features of `loopy'.
:END:

#+cindex: loop command
If a macro argument does not match one of the previously listed special macro
arguments ([[#macro-arguments][Special Macro Arguments]]), ~loopy~ will attempt to treat it as a loop
command.  Loop commands are only valid as a top-level argument to the macro, or
inside another loop command.

Therefore, these macro calls are valid:

#+BEGIN_SRC emacs-lisp
  (loopy (list i '(1 2 3))
         (collect coll i)
         ;; Special macro argument:
         (finally-return coll))

  ;; Implicit accumulation variable and implicit return value:
  (loopy (list i '(1 2 3))
         (collect i))
#+END_SRC

and this is not:

#+BEGIN_SRC emacs-lisp
  (loopy (with (list i '(1 2 3)))
         (finally-return (collect coll i)))
#+END_SRC

Trying to use loop commands in places where they don't belong will result in
errors while the macro is expanding and when the code is evaluated.

You should keep in mind that commands are evaluated in order.  This means that
attempting something like the below example might not do what you expect, as =i=
is assigned a value from the list after collecting =i= into =coll=.

#+caption: An example of how loop commands are evaluated in order.
#+BEGIN_SRC emacs-lisp
  ;; => (nil 1 2)
  (loopy (collect coll i)
         (list i '(1 2 3))
         (finally-return coll))
#+END_SRC

For convenience and understanding, the same command might have multiple names,
called {{{dfn(aliases)}}}.  For example, the =array= command has the alias
=string=, because the =array= command can be used to iterate through the
elements of an array or string[fn:1].  Implementation-wise, there is no longer a
difference between a loop command's preferred name (which are the ones commonly
used and listed first in this document) and its aliases.  We continue to use the
word "alias" for its common definition rather than to suggest a difference in
the code.  Both preferred names and aliases are found in the customizable
variable ~loopy-parsers~.  You can define custom aliases by modifying this
variable ([[#custom-aliases][Custom Aliases]]).

Similar to other libraries, many commands have an alias of the
present-participle form (the "-ing" form).  A few examples are seen in the table
below.

| Command   | "-ing" Alias |
|-----------+--------------|
| =set=     | =setting=    |
| =list=    | =listing=    |
| =collect= | =collecting= |
| =numbers= | =numbering=  |

Some commands take optional keyword arguments.  For example, the command =list=
can take a function argument following the keyword =:by=, which affects how that
command iterates through the elements in the list.

For simplicity, the commands are described using the following notation:

- If a command has multiple names, the names are separated by a vertical bar,
  such as in =array|string=.
- =VAR= is an unquoted symbol that will be used as a variable name, such as =i=
  in =(list i my-list)=.
- =FUNC= is an expression that evaluates to a function or symbol naming a
  function, such as ~#'my-func~, ~'my-func~, a variable whose value is a
  function, or a ~lambda~ expression.
- =NAME= is an unquoted name of a loop (or, more accurately, of a ~cl-block~).
- =EXPR= is a single Lisp expression, such as =(+ 1 2)=, ='(1 2 3)=, =my-var=,
  or =(some-function my-var)=.  =EXPRS= means multiple expressions.  Really, we
  are concerned with the value of the expression, not the expression itself.
- =CMD= is a loop command, as opposed to a normal Lisp expression.
  =(list i '(1 2 3))=, =(cycle 5)=, and =(return-from outer-loop 7)=
  are examples of loop commands.  =CMDS= means multiple commands.
- Optional arguments are surround by brackets.  =[EXPR]= is an optional
  expression, and =[CMD]= is an optional command.  By extension, =[EXPRS]= is
  equivalent to =[EXPR [EXPR [...]]]=, and =[CMDS]= to =[CMD [CMD [...]]]=.
- Optional keyword arguments are shown as =&key key1 key2 ...=, where =key1=,
  =key2=, and so on are the literal keywords.  Just like in normal Lisp
  functions, command keywords must be prefixed by a colon (":").  For example,
  the iteration command =list= has a keyword argument =by=, which can be given a
  value using =:by SOME-EXPRESSION=.


Generally, =VAR= is initialized to ~nil~, but not always.  This document tries
to note when that is not the case.  For when that is not the case, the variable
can still be initialized to ~nil~ if it is set to ~nil~ using the =with= special
macro argument.  These special cases allow for more efficient code and less
indirection.

#+begin_src emacs-lisp
  ;; => (0 1 2 3)
  (loopy (collect i)
         (numbers i :from 0 :to 3))

  ;; => (nil 0 1 2)
  (loopy (with (i nil))
         (collect i)
         (numbers i :from 0 :to 3))
#+end_src

#+cindex: keyword evaluation
Unlike ~cl-loop~ in some cases, in Loopy, the values passed as keyword arguments
are evaluated only once.  For example, the command =(list i some-list :by
(get-function))= evaluates ~(get-function)~ only once.  It does not evaluate it
repeatedly for each step of the loop.

#+begin_src emacs-lisp
  ;; Passes the assertion:
  ;;
  ;; => (0 1 2 3 4 5 6 7 8 9 10)
  (loopy (with (times 0))
         (list i (number-sequence 0 10) :by (progn
                                              (cl-assert (= times 0))
                                              (cl-incf times)
                                              #'cdr))
         (collect i))

  ;; => Fails the assertion on the second step of the loop:
  (cl-loop with times = 0
           for i in (number-sequence 0 10) by (progn
                                                (cl-assert (= times 0))
                                                (cl-incf times)
                                                #'cdr)
           collect i)
#+end_src

** Basic Destructuring
:PROPERTIES:
:CUSTOM_ID: basic-destructuring
:DESCRIPTION: How to destructure variables and values in loop commands.
:END:

#+cindex: variable destructuring
Similar to features like ~seq-let~, ~cl-destructuring-bind~, and ~pcase-let~,
~loopy~ is capable of destructuring values when assigning values to variables.
Destructuring in Loopy is similar to, but more featureful than, what is
provided in =cl-lib=.

Some differences include:
- Destructuring arrays

- Destructuring in accumulation commands ([[#accumulation-commands]])

- Destructuring in commands iterating through ~setf~-able places in a sequence
  ([[#sequence-reference-iteration]])

- The extended forms of the =&optional= and =&key= variables (such as default
  values like in ~... &optional (var default) ...~) can be specified using
  square brackets as well as parentheses (such as ~... &optional [var default]
  ...~).  Since such variables can be further destructured by being written as
  sequences themselves, allowing both parentheses and brackets reduces confusion
  and improves consistency.

- A =&map= construct, similar to =&key=, but using ~map-elt~ instead of
  ~plist-get~ and which does not error when the map contains keys which aren't
  matched (in other words, there is no need for an equivalent of
  =&allow-other-keys=).


This section describes the basic built-in destructuring used by most loop
commands, such as =set= and =list=.  Destructuring in accumulation commands
([[#accumulation-commands]]) and sequence-reference commands
([[#sequence-reference-iteration]]) works slightly differently, and is described
more in those sections.

#+cindex: pcase pattern loopy
In addition to what can be done in loop commands, several features are available
for using Loopy's destructuring outside of ~loopy~ loops ([[#destr-macros]]),
including the ~pcase~ pattern =loopy=.

The last thing to note is that ~loopy~ loops can be made to use alternative
destructuring systems, such as ~seq-let~ or ~pcase-let~.  This is done by using
the =flag= special macro argument ([[#flags]]).  If you are familiar with the
package =dash= [fn:dash] and its Clojure-style destructuring, consider trying
the flag =dash= provided by the package =loopy-dash=.


Below are two examples of destructuring in ~cl-loop~ and ~loopy~.

#+caption: Destructuring values in a list.
#+begin_src emacs-lisp
  ;; => (1 2 3 4)
  (cl-loop for (i . j) in '((1 . 2) (3 . 4))
           collect i
           collect j)

  ;; => (1 2 3 4)
  (loopy (list (i . j) '((1 . 2) (3 . 4)))
         (collect i)
         (collect j))
#+end_src

#+caption: Destructuring values in assignment.
#+begin_src emacs-lisp
  ;; => (1 2 3 4)
  (cl-loop for elem in '((1 . 2) (3 . 4))
           for (i . j) = elem
           collect i
           collect j)

  ;; => (1 2 3 4)
  (loopy (list elem '((1 . 2) (3 . 4)))
         (set (i . j) elem)
         (collect i)
         (collect j))
#+end_src


You can use destructured assignment by passing an unquoted sequence of symbols
as the =VAR= argument of a loop command.  Loopy supports destructuring lists,
arrays (which includes strings and vectors), and generic sequences as defined by
the =seq.el= library.

- To destructure lists, use a list, as in =(a b c)=.

- To destructure arrays, use a vector, as in =[a b c]=.

- To destructure sequences generically using =seq.el= (mainly via ~seq-elt~ and
  ~seq-drop~), use a vector or a list whose first element is =&seq=, as in
  =[&seq a b c]= and =(&seq a b c)=.


The sequence of symbols can receive less values than there are in the sequence,
/but not more/.  If less, the remainder of the sequence is simply ignored.  If
more, than an error is signalled.  Note that this differs from ~seq-let~, which
can bind unfound values to ~nil~ in some cases, as seen below.

#+begin_src emacs-lisp
  ;; Ignores third element of the vector:
  ;;
  ;; => ((1 4) (2 5))
  (loopy (list [a b] '([1 2 3] [4 5 6]))
         (collect as a)
         (collect bs b)
         (finally-return as bs))

  ;; Signals an error:
  (loopy (list [a b c] '([1 2] [3 4]))
         (collect as a)
         (collect bs b)
         (collect cs c)
         (finally-return as bs cs))

  ;; Does not signal an error:
  ;;
  ;; (1 2 nil)
  (seq-let (a b c)
      [1 2]
    (list a b c))

  ;; Signals an error:
  (loopy (list (&seq a b c) '([1 2] [3 4]))
         (collect as a)
         (collect bs b)
         (collect cs c)
         (finally-return as bs cs))
#+end_src

The content of this destructuring sequence is similar to =cl-lib=, and is

#+begin_example
  [&seq]
  [&whole WHOLE-VARIABLE]
  [POSITIONAL-VARIABLES]
  [&optional OPTIONAL-VARIABLES]
  [&rest REST-VARIABLE]
  [&key KEY-VARIABLES [&allow-other-keys]]
  [&map MAP-VARIABLES]
  [&aux AUXILLIARY-VARIABLES]
#+end_example

in which at least one of the above constructs that binds variables must be
provided.

#+begin_src emacs-lisp
  ;; => (1 2 3
  ;;     4 5 t
  ;;     (:k1 111 :k2 222)
  ;;     111 t
  ;;     222
  ;;     111
  ;;     333 nil
  ;;     4444 5555)
  (pcase (list 1 2 3 4 5 :k1 111 :k2 222)
    ((loopy ( a b c
              &optional
              d
              (e nil e-supplied)
              &rest
              r
              &key
              ((:k1 k1) nil k1-supplied)
              k2
              &map
              (:k1 map1)
              [:k3 map3 333 map3-supplied]
              &aux
              [x1 4444] (x2 5555)))
     (list a b c
           d
           e e-supplied
           r
           k1 k1-supplied
           k2
           map1
           map3 map3-supplied
           x1 x2)))
#+end_src


In more detail, the elements of the destructuring sequence can be:

- A positional variable which will be bound to the corresponding element in the
  sequence.  These variables can themselves be sequences, but must be of the
  correct type.

  #+begin_src emacs-lisp
    ;; ((1 2 3) (4 5 6))
    (loopy (list [i (j k)] '([1 (2 3)] [4 (5 6)]))
           (collect (list i j k)))
  #+end_src

#+cindex: _
- The symbol =_= (an underscore) or a symbol beginning with an underscore: This
  means to ignore the element at this location.  This can be more efficient.

  #+begin_src emacs-lisp
    ;; Only creates the variables `a' and `d':
    ;; => ((1 4) (5 8))
    (loopy (list (a _ _ d) '((1 2 3 4) (5 6 7 8)))
           (collect (list a d)))

    ;; These two destructurings do the same thing,
    ;; and only bind the variable `a':
    ;;
    ;; => (1 3)
    (loopy (array (a) [(1 2) (3 4)])
           (collect a))

    ;; => (1 3)
    (loopy (array (a . _ignored) [(1 2) (3 4)])
           (collect a))
  #+end_src

#+cindex: &whole
- The symbol =&whole=: If =&whole= is the first element in the sequence (or the
  second element if =&seq= is the first), then the following element of the
  sequence names a variable that holds the entire value of what is destructured.

  This is the same as when used in a CL ~lambda~ list.

  #+begin_src emacs-lisp
    ;; See that the variable `both' holds the value of the entire
    ;; list element:
    ;;
    ;; => (((1 2) 1 2)
    ;;     ((3 4) 3 4))
    (loopy (list (&whole both i j)  '((1 2) (3 4)))
           (collect (list both i j)))

    (mapcar (cl-function (lambda ((&whole both i j))
                           (list both i j)))
            '((1 2) (3 4)))
  #+end_src

#+cindex: &rest
- The symbol =&rest=: A variable named after =&rest= contains the remaining
  elements of the destructured value after any positional and optional values.
  When destructuring lists, one can also use dotted notation, as in a CL
  ~lambda~ list.  These variables can themselves be sequences to be further
  destructured.

  When used after optional values, the =&rest= value is the sub-sequence starting
  at the index after any possible optional values, even when those optional
  values are not actually present.  If the sequence is not long enough, then the
  sub-sequence is empty.

  #+begin_src emacs-lisp
    ;; => (1 2 (3))
    (pcase (list 1 2 3)
      ((loopy (a &optional b &rest c))
       (list a b c)))

    ;; => (1 nil nil)
    (pcase (list 1)
      ((loopy (a &optional b &rest c))
       (list a b c)))

    ;; => (1 [])
    (pcase (vector 1)
      ((loopy [a &optional _ _ _ _ &rest c])
       (list a c)))
  #+end_src

  This =&rest= is the same as when used in ~seq-let~.

  #+begin_src emacs-lisp
    ;; => ((1 [2 3]) (4 [5 6]))
    (loopy (list [i &rest j] '([1 2 3] [4 5 6]))
           (collect (list i j)))

    ;; => ((1 2 3) (4 5 6))
    (loopy (list [i &rest [j k]] '([1 2 3] [4 5 6]))
           (collect (list i j k)))

    ;; => ((1 (2 3)) (4 (5 6)))
    (loopy (list (i &rest j) '((1 2 3) (4 5 6)))
           (collect (list i j)))

    ;; => ((1 2 3) (4 5 6))
    (loopy (list (i &rest (j k)) '((1 2 3) (4 5 6)))
           (collect (list i j k)))

    ;; => ((1 2 3) (4 5 6))
    (loopy (list (i . (j k)) '((1 2 3) (4 5 6)))
           (collect (list i j k)))

    ;; => ((1 2 3) (4 5 6))
    (loopy (list (i &rest [j k]) '((1 . [2 3]) (4 . [5 6])))
           (collect (list i j k)))

    ;; => ((1 2 3) (4 5 6))
    (loopy (list (i . [j k]) '((1 . [2 3]) (4 . [5 6])))
           (collect (list i j)))
  #+end_src

#+cindex: &optional
- The symbol =&optional=: A variable named after =&optional= is bound if the
  sequence is long enough to have a value at that position.  If the sequence is
  not long enough, then the variable is bound to ~nil~ or, if specified, a
  default value.  Additionally, one may bind a variable to record whether the
  sequence was long enough to contain the optional value.

  As in a CL ~lambda~ list, the variable has the one of the following forms:

  - =(VAR DEFAULT SUPPLIED)= or =[VAR DEFAULT SUPPLIED]=, in which =VAR= itself
    can be a sequence

  - =(VAR DEFAULT)= or =[VAR DEFAULT]=, in which =VAR= itself can be a sequence

  - =(VAR)= or =[VAR]=, in which =VAR= itself can be a sequence

  - a symbol =VAR=

  #+begin_src emacs-lisp
    ;; => (1 2 88 t nil)
    (loopy (array (a &optional ((b &optional (c 88 c-supplied))
                                (list 77)
                                bc-supplied))
                  [(1 (2))])
           (collect (list a b c bc-supplied c-supplied)))

    ;; => (1 2 3 t t)
    (loopy (array (a &optional ((b &optional (c 88 c-supplied))
                                (list 77)
                                bc-supplied))
                  [(1 (2 3))])
           (collect (list a b c bc-supplied c-supplied)))
  #+end_src

  =&optional= cannot be used after =&rest=.

  #+begin_src emacs-lisp
    ;; => ((1 2 3 4 5)
    ;;     1
    ;;     2
    ;;     3
    ;;     (4 5))
    (loopy (array (&whole all a b &optional c &rest d)
                  [(1 2 3 4 5)])
           (collect (list all a b c d)))

    ;; Same as above:
    (loopy (array (&whole all a b &rest (c &rest d))
                  [(1 2 3 4 5)])
           (collect (list all a b c d)))
  #+end_src

#+cindex: &key
#+cindex: &keys
- The symbol =&key= or =&keys=: Variables named after =&key= are transformed
  into keys whose values will be sought using ~plist-get~, which returns ~nil~
  if the key isn't found in the list.

  Only lists support this destructuring.

  #+begin_src emacs-lisp
    ;; => ((1 2 nil) (4 5 nil))
    (loopy (list (&key a b missing) '((:b 2 :c 3 :a 1)
                                      (:a 4 :b 5 :c 6)))
           (collect (list a b missing)))
  #+end_src

  Variables after =&key= can be of the following forms:

  - =((VAR KEY) DEFAULT SUPPLIED)=, =[[VAR KEY] DEFAULT SUPPLIED]=, =([VAR KEY]
    DEFAULT SUPPLIED)=, or =[(VAR KEY) DEFAULT SUPPLIED]=, in which =VAR= itself
    can be a sequence

  - =((VAR KEY) DEFAULT)=, =[[VAR KEY] DEFAULT]=, =([VAR KEY] DEFAULT)=, or
    =[(VAR KEY) DEFAULT]=, in which =VAR= itself can be a sequence

  - =((VAR KEY))=, =[[VAR KEY]]=, =([VAR KEY])=, or =[(VAR KEY)]=, in which
    =VAR= itself can be a sequence

  - =(VAR DEFAULT SUPPLIED)= or =[VAR DEFAULT SUPPLIED]=, in which =VAR= is a
    symbol

  - =(VAR DEFAULT)= or =[VAR DEFAULT]=, in which =VAR= is a symbol

  - =(VAR)= or =[VAR]=, in which =VAR= is a symbol

  - a symbol =VAR=

  If a default value is provided, then keys are sought using ~plist-member~.
  That way, a value of ~nil~ for a key is not the same as a missing key.

  #+begin_src emacs-lisp
    ;; Note that `nil' is not the same as a missing value:
    ;;
    ;; => ((1 2 nil 25) (4 5 24 25))
    (loopy (list (&key a b (c 24) (missing 25)) '((:b 2 :c nil :a 1)
                                                  (:a 4 :b 5)))
           (collect (list a b c missing)))
  #+end_src

  By default, the sought key is made by prepending a colon (":") to the symbol
  name.  For example, =a= searches for =:a= and =b= searches for =:b=.  Like in
  =cl-lib=, an evaluated key can be sought by using a sub-sequence as the first
  element of the list.  When =VAR= is a sequence, the key must be provided
  separately.

  #+begin_src emacs-lisp
    ;; => ((1 nil t))
    (loopy (list (&key ((:cat c)) ((:dog d) 27 dog-found))
                 '((:cat 1 :dog nil)))
           (collect (list c d dog-found)))
  #+end_src

  Keys are sought in values after those bound to positional variables, which can
  be the same values bound to the variable named by =&rest= when both are
  used.

  #+begin_src emacs-lisp
    ;; Keys are only sought after positional variables:
    ;;
    ;; => ((1 2 :k1 'ignored 3))
    (loopy (array (a b c d &key k1) [(1 2 :k1 'ignored :k1 3)])
           (collect (list a b c d k1)))

    ;; If `&rest' is used, keys are sought only in that variable:
    ;;
    ;; => ((1 (:k1 3) 3))
    (loopy (array (a &rest b &key k1) [(1 :k1 3)])
           (collect (list a b k1)))
  #+end_src

  =&key= and =&rest= can be used in any order, but =&key= must come before
  the dot in dotted lists.

  #+begin_src emacs-lisp
    ;; => ((1 (:k1 3) 3))
    (loopy (array (a &rest b &key k1) [(1 :k1 3)])
           (collect (list a b k1)))

    (loopy (array (a &key k1 &rest b) [(1 :k1 3)])
           (collect (list a b k1)))

    (loopy (array (a &key k1 . b) [(1 :k1 3)])
           (collect (list a b k1)))
  #+end_src

  Like in =cl-lib=, if, after searching for the other keys, there remains an
  unmatched key in the destructured value, an error is signaled unless
  =&allow-other-keys= is also used, or unless the key =:allow-other-keys= is
  associated with a non-nil value in the property list.

  #+begin_src emacs-lisp
    ;; Error due to presence of `:k3':
    (cl-destructuring-bind (a b &rest c &key k1 k2)
        (list 1 2 :k1 3 :k2 4 :k3 5)
      (list a b c k1 k2))

    ;; Works as expected:
    ;;
    ;; => (1 2 (:k1 3 :k2 4 :k3 5) 3 4)
    (cl-destructuring-bind (a b &rest c &key k1 k2 &allow-other-keys)
        (list 1 2 :k1 3 :k2 4 :k3 5)
      (list a b c k1 k2))
  #+end_src

#+cindex: &map
- The symbol =&map=: Variables after =&map= are bound similarly to ~map-let~
  from the library =map.el=.  =&map= works similarly to =&key=, but has a few
  important differences:

  1. Maps are more generic than property lists ("plists").  A "map" is a generic
     structure which supports the function ~map-elt~.  The built-in maps are
     arrays, property lists ("plists"), association lists ("alists"), and hash
     tables.  This generality means that it is slower than =&key= for property
     lists, though the difference should be small.

  2. =&map= will not signal an error if there are unused keys inside the
     destructured value; there is no =&allow-other-keys= for =map=.  In the same
     vein, it cannot be made to signal an error if there are unused keys.

  Variables after =&map= can be of the following forms:

  - =(KEY VAR DEFAULT SUPPLIED)= or =[KEY VAR DEFAULT SUPPLIED]=, in which =VAR=
    itself can be a sequence

  - =(KEY VAR DEFAULT)= or =[KEY VAR DEFAULT]=, in which =VAR= itself can be a
    sequence

  - =(KEY VAR)= or =[KEY VAR]=, in which =VAR= itself can be a sequence

  - =(VAR)= or =[VAR]=, in which =VAR= must be a _symbol_

  - a symbol =VAR=

  When =KEY= is not given, then the key is the symbol =VAR=, as in ~(quote
  VAR)~.  Unlike with =&key=, it is _not_ prepended with a colon.

  #+begin_src emacs-lisp
    ;; => ((1 2 3 4 27))
    (loopy (array (a b &map c ('dog d) (:elephant e 27))
                  [(1 2 c 3 dog 4)])
           (collect (list a b c d e)))

    ;; => ((1 2 3 4 27 33 nil))
    (loopy (array ( a b
                    &map
                    c
                    ('dog d)
                    (:elephant e 27)
                    (:fox f 33 fox-found))
                  [(1 2 (c . 3) (dog . 4))])
           (collect (list a b c d e f fox-found)))

    ;; => ((1 2 5 t))
    (loopy (array (a b &map (:fox f 33 fox-found))
                  [(1 2 (c . 3) (dog . 4) (:fox . 5))])
           (collect (list a b f fox-found)))

    ;; For arrays, the key is the index:
    ;;
    ;; => ((20 50))
    (loopy (list (&map (2 two-times-ten) (5 five-times-ten))
                 (list  [00 10 20 30 40 50 60 70 80 90 100]))
           (collect (list two-times-ten five-times-ten)))
  #+end_src

  When =&map= and =&key= are used together, they search through the same
  values.  The use of both is normally redundant.

  #+begin_src emacs-lisp
    ;; => (1 2 (:k1 3 :k2 4)
    ;;     3 4
    ;;     3 4)
    (loopy (array ( a b
                    &rest c
                    &key ((:k1 key-k1)) ((:k2 key-k2))
                    &map (:k1 map-k1) (:k2 map-k2))
                  [(1 2 :k1 3 :k2 4)])
           (collect (list a b c
                          key-k1 key-k2
                          map-k1 map-k2)))
  #+end_src

- The symbol =&aux=: Variables named after =&aux= are bound to the given values.
  Like in CL Lib, =&aux= must come last in the sequence.

  #+begin_src emacs-lisp
    ;; => (7 7 7)
    (loopy (cycle 3)
           (collect (&aux [coll 7]) 'ignored)
           (finally-return coll))
  #+end_src

- The symbol =&seq=: If the first symbol in the sequence is =&seq=, then the
  sequence will be destructured as a generic sequence using the generic-sequence
  library =seq.el=.  Specifically, destructuring is similar to using ~seq-elt~
  and ~seq-drop~.  This form is less efficient than destructuring a sequence as
  an array or as a list, when applicable.

  Sequences destructured using =&seq= can still use =&whole=, =&optional=,
  =&rest=, and =&map=.  However, note that because the type of the destructured
  sequence is not known ahead of time, =&key= cannot be used with =&seq=.

  #+begin_src emacs-lisp
    ;; => ((0 1 2 nil nil)
    ;;     (3 4 5 [6 7])
    ;;     (?a ?b ?c ""))
    (loopy (list [&seq i j &optional k &rest r] '((0 1) [3 4 5 6 7] "abc"))
           (collect (list i j k r)))
  #+end_src


** Generic Evaluation
:PROPERTIES:
:DESCRIPTION: Setting variables, evaluating expressions, etc.
:CUSTOM_ID: commands-for-generic-evaluation
:END:

These generic commands are for settings values and running sub-commands or
sub-expressions during the loop.  These commands do not affect the macro's
return value and do no affect how the loop iterates.

#+tindex: do
- =(do EXPRS)= :: Evaluate multiple Lisp expressions, like a =progn=.

  You cannot include arbitrary code in the loop body in ~loopy~.  Trying to do
  so will result in errors, as the macro will attempt to interpret such code as
  a loop command.

  To use loop commands in arbitrary code, use the macro ~loopy-iter~ instead
  ([[#loopy-iter]]).

  #+BEGIN_SRC emacs-lisp
    (loopy (list i '(1 2 3))
           (do (message "%d" i)))
  #+END_SRC

#+tindex: command-do
- =(command-do [CMDS])= :: Evaluate multiple loop commands, as if in a
  =progn=.  This is similar to =do=, but runs commands instead of normal Lisp
  expressions.  Currently, this command is only useful when used within the
  =if= command.

  #+begin_src emacs-lisp
    ;; Report whether an even number is found, or return the sum of
    ;; the list's elements.  To be clear, this is not an idiomatic example.

    ;; Returns the detected even number:
    ;; => 2
    (loopy (list i '(1 3 2 5))
           (if (cl-evenp i)
               (command-do
                (do (message "Even found."))
                (return i))
             (sum i)))
  #+end_src

#+tindex: set
#+tindex: setting
- =(set VAR EXPR [EXPRS])= :: Bind =VAR= to each =EXPR=, in order. In the n-th
  cycle of the loop, bind =VAR= to the n-th =EXPR=.  Once the last
  =EXPR= is reached, it is used repeatedly for the rest of the loop.

  This command also has the aliases =setting=.

  Unlike the Emacs Lisp function ~set~, the variable name should not be quoted.
  Unlike the Emacs Lisp special form ~setq~, the command =set= only sets one
  variable, and this variable is by default ~let~-bound around the loop.  To
  stop =VAR= from being ~let~-bound around the loop, use the special macro
  argument =without= ([[#macro-arguments]]).

  #+BEGIN_SRC emacs-lisp
    ;; => '(1 2 3 3 3)
    (loopy (cycle 5)
           (set i 1 2 3)
           (collect coll i)
           (finally-return coll))

    ;; => '(0 1 2 3 4)
    (loopy (cycle 5)
           (set i 0 (1+ i))
           (collect coll i)
           (finally-return coll))
  #+END_SRC

#+tindex: set-prev
#+tindex: setting-prev
#+tindex: prev-set
- =(set-prev VAR VAL &key back)= :: Bind =VAR= to a value =VAL= from a previous
  cycle in the loop.  With =BACK= (default: 1), use the value from that many
  cycles previous.  _If not enough cycles have passed yet, then the value of
  =VAR= is not modified._  This command /does not/ work like a queue for
  recording =VAL=; it always uses the value from the =BACK=-th previous cycle,
  regardless of when the command is run.  The value used is always the value at
  the end of the cycle.

  This command also has the aliases =setting-prev= and, for typo tolerance,
  =prev-set=.

  #+begin_src emacs-lisp
    ;; => (nil 1 2 3 4)
    (loopy (list i '(1 2 3 4 5))
           (set-prev j i)
           (collect j))

    ;; => (nil nil nil 1 2)
    (loopy (with (n 3))
           (list i '(1 2 3 4 5))
           (set-prev j i :back n)
           (collect j))

    ;; NOTE: `j' isn't overwritten until the correct cycle:
    ;;
    ;; => ((first-val nil) (first-val nil) (1 2) (3 4))
    (loopy (with (j 'first-val))
           (list i '((1 . 2) (3 . 4) (5 . 6) (7 . 8)))
           (set-prev (j . k) i :back 2)
           (collect (list j k)))

    ;; NOTE: `prev-expr' keeps track of the previous value of `i',
    ;;       even when `j' isn't updated.
    ;;
    ;; => (first-val first-val 2 2 4 4 6 6 8 8)
    (loopy (with (j 'first-val))
           (numbers i :from 1 :to 10)
           (when (cl-oddp i)
             (set-prev j i))
           (collect j))

    ;; NOTE: `j' is always bound to the previous value of `i'
    ;;       from the end of the specified cycle.
    ;;
    ;; => (nil 101 102 103)
    (loopy (numbers i :from 1 :to 4)
           (set i2 i)
           (set-prev j i2)
           (set i2 (+ i 100))
           (collect j))
  #+end_src

** Iteration
:PROPERTIES:
:CUSTOM_ID: iteration-and-looping-commands
:DESCRIPTION: Iterating through sequences, etc.
:END:

{{{dfn(Iteration commands)}}} bind local variables and determine when the loop
ends.  If no command sets an ending condition, then the loop runs forever.
Infinite loops can be exited by using early-exit commands
([[#exiting-the-loop-early]]) or boolean commands ([[#boolean-commands]]).

Iteration commands must occur in the top level of the macro or in the top level
of a sub-loop command ([[#sub-loops]]).  Using them elsewhere and trying to do
something like the below example will signal an error.

#+begin_src emacs-lisp
  ;; Signals an error:
  (loopy (list i '(1 2 3 4 5))
         (when (cl-evenp i)
           ;; Can't use `list' inside `when'.
           ;; Will signal an error.
           (list j '(6 7 8 9 10))
           (collect j)))
#+end_src

In ~loopy~, iteration commands are named after what they iterate through.  For
example, the =array= and =list= commands iterate through the elements of arrays
and lists, respectively.

Because some iteration commands use their variable to manage state, it is an
error to use the same iteration variable for multiple iteration commands.

#+begin_src emacs-lisp
  ;; Signals an error due to the re-use of `i':
  (loopy (numbers i :from 1 :to 10)
         (list i '(1 2 3))
         (finally-return t))
#+end_src

Iteration variables are initialized to ~nil~ and they are updated at the point
in the loop body corresponding to the loop command's position in the macro's
arguments.

#+begin_src emacs-lisp
  ;; `elem' retains its value from the previous
  ;; iteration until it is updated again:
  ;;
  ;; => (((1 . nil) ; before
  ;;      (2 . 1)
  ;;      (3 . 2)
  ;;      (4 . 3))
  ;;     ((1 . 1) ; after
  ;;      (2 . 2)
  ;;      (3 . 3)
  ;;      (4 . 4)))
  (loopy (numbers nth :from 1)
         (collect elem-before (cons nth elem))
         (list elem '(1 2 3 4))
         (collect elem-after (cons nth elem))
         (finally-return elem-before
                         elem-after))
#+end_src

Be aware that ~cl-loop~ does not consistently initialize its iteration variables
to nil.  For some of ~cl-loop~'s iteration (=for=) statements, the variable is
initialized to its value for the first iteration step and is manipulated
directly at the end of the iteration step.  Loopy avoids this, as seen in the
below example, but that can result in unnecessary indirection for some use
cases, which has a minor speed cost.

#+begin_src emacs-lisp
  ;; => (5 (1 2 3 4) (1 2 3 4))
  (cl-loop for elem in (list 1 2 3 4)
           collect num into nums-1
           for num from 1
           collect num into nums-2
           finally return (list num nums-1 nums-2))

  ;; => (4 (nil 1 2 3) (1 2 3 4))
  (loopy (list elem (list 1 2 3 4))
         (collect nums-1 num)
         (numbers num :from 1)
         (collect nums-2 num)
         (finally-return num nums-1 nums-2))
#+end_src

Generally, iteration commands with conditions check whether to terminate the
loop /before/ the next iteration is run.  They do not check their conditions
while running the current iteration step.  In the below example, note that the
final value of ~i~ is 2 and not 3, even though the =do= command (similar to
~cl-loop~'s =do= keyword) is placed before the =list= command.  Even though ~i~
is updated before ~elem~ is updated, the decision whether to continue the loop,
based on the =list= command's condition, is made /before/ the code in the =do=
command is run.

#+begin_src emacs-lisp
  ;; => 2, not 3
  (let ((i 0))
    (loopy (do (setq i (1+ i)))
           (list elem '(0 1)))
    i)
#+end_src

If you do wish to conditionally leave the loop during an iteration, consider
using the =leave= and =leave-from= commands ([[#exiting-the-loop-early]]).

#+begin_src emacs-lisp
  ;; => (3 (0 1))
  (loopy (with (some-list (list 0 1))
               (i 0))
         (do (setq i (1+ i)))
         (when (null some-list)
           (leave))
         (collect elems (car some-list))
         (do (setq some-list (cdr some-list)))
         (finally-return i elems))
#+end_src

Unlike ~cl-loop~ and like Common Lisp's ~iterate~, arguments of the iteration
commands are evaluated only once.  For example, while iterating through numbers,
you can't suddenly change the direction of the iteration in the middle of the
loop, nor can you change the final numeric value.  Similarly, the function used
to iterate through the list in the =list= command is the same for the entire
loop.  This restriction allows for producing more efficient code.


*** Generic Iteration
:PROPERTIES:
:CUSTOM_ID: generic-iteration
:DESCRIPTION: Looping a certain number of times.
:END:

#+tindex: cycle
#+tindex: cycling
#+tindex: repeat
#+tindex: repeating
- =(cycle|repeat [VAR] EXPR)= :: Run the loop for =EXPR= iterations.

  If given, then during the loop, =VAR= is set to the number of iteration steps
  that have been run (0 for the first iteration step).

  If =EXPR= is 0, then the loop isn't run.

  =(cycle VAR EXPR)= works the same as =(numbers VAR :from 0 :below EXPR)=
  ([[#numeric-iteration]]).

  This command also has the aliases =cycling= and =repeating=.

  #+BEGIN_SRC emacs-lisp
    ;; => (10 10 10)
    (loopy (with (i 10))
           (cycle 3)
           (collect i))

    ;; => (10 0 10 1 10 2)
    (loopy (with (i 10))
           (repeat j 3)
           (collect i)
           (collect j))

    ;; Same as above:
    ;;
    ;; => (10 0 10 1 10 2)
    (loopy (with (i 10))
           (numbers j :from 0 :below 3)
           (collect i)
           (collect j))

    ;; An argument of 0 stops the loop from running:
    ;; => nil
    (loopy (cycle 0)
           (return 'return-command-ran))
  #+END_SRC

#+tindex: iter
#+tindex: iterating
- =(iter [VAR] EXPR &key close yield-result)= :: Iterate through the values
  returned by an Emacs Lisp iterator ([[info:elisp#Generators]]).  =EXPR= is an
  iterator object produced by a calling a generator function.  If given, =VAR=
  holds the value yielded by the iterator. The loop ends when the iterator
  finishes.

  =close= is whether the generator should be closed via ~iter-close~ after the
  loop ends.  The default is ~t~.  Note that Emacs will eventually close
  un-closed, un-reachable generators during garbage collection.  To be
  consistent with other commands, =close= is evaluated at the start of the loop,
  even though it's value is only used after the loop finishes.

  =yield-result= is the optional second argument to the function ~iter-next~,
  which is the value of ~iter-yield~ in the iterator (not to be confused with
  the value yielded by calling ~iter-next~).  Unlike =close=, which is evaluated
  once, =yield-result= is an expression which is substituted into the loop body.
  Therefore, =yield-result= can be used to repeatedly call functions.

  This command also has the name =iterating=.

  #+begin_src emacs-lisp
    ;; With var:
    ;;
    ;; => ((1 . 4) (2 . 5) (3 . 6))
    (loopy (with (iter-maker (iter-lambda (x)
                               (while x
                                 (iter-yield (pop x))))))
           (iter i (funcall iter-maker (list 1 2 3)))
           (iter j (funcall iter-maker (list 4 5 6)))
           (collect (cons i j)))

    ;; Without var:
    ;;
    ;; => (1 2 3)
    (loopy (iter (funcall (iter-lambda ()
                            ;; These yielded values are all ignored.
                            (iter-yield 'first-yield)
                            (iter-yield 'second-yield)
                            (iter-yield 'third-yield))))
           (set i 1 (1+ i))
           (collect i))

    ;; Using `yield-result':
    ;;
    ;; => (3 2 1)
    (loopy (with (yield-results nil))
           (set i 1 (1+ i))
           (iter (funcall (iter-lambda ()
                            ;; The value from the expression specified by
                            ;; `:yield-result' is `push'-ed:
                            (push (iter-yield 'first-yield) yield-results)
                            (push (iter-yield 'second-yield) yield-results)
                            (push (iter-yield 'third-yield) yield-results)))
                 ;; Note that the value of `i' evaluated each time:
                 :yield-result i)
           (finally-return yield-results))
  #+end_src

  #+ATTR_TEXINFO: :tag Warning
  #+begin_quote
  Be aware that values are yielded from the iterator before running the loop
  body.  When the iterator can no longer yield values, it is finished.

  Because values are yielded before the next iteration step of the loop, trying
  to yield more values from the iterator after the loop ends will result in lost
  values.  One option for working around this is to use the generic command
  =set= with the function ~iter-next~ directly.
  #+end_quote

  #+begin_src emacs-lisp
    ;; => 5, not 4 as one might expect.
    (loopy (with (iter-obj (funcall (iter-lambda ()
                                      (let ((i 0))
                                        (while t
                                          (iter-yield (cl-incf i))))))))
           (iter iter-obj :close nil)
           (cycle 3)
           (finally-return (prog1 (iter-next iter-obj)
                             (iter-close iter-obj))))

    ;; Avoiding missed yielded values:
    ;;
    ;; => ((1 2 3) 4)
    (loopy (with (iter-obj (funcall (iter-lambda ()
                                      (let ((i 0))
                                        (while t
                                          (iter-yield (cl-incf i)))))))
                 (j nil))
           (cycle 3)
           (set j (condition-case nil
                      (iter-next iter-obj)
                    (iter-end-of-sequence nil)))
           (collect j)
           (finally-return (prog1
                               (list loopy-result (iter-next iter-obj))
                             (iter-close iter-obj))))
  #+end_src


*** Numeric Iteration
:PROPERTIES:
:CUSTOM_ID: numeric-iteration
:DESCRIPTION: Iterating through numbers.
:END:

For iterating through numbers, there is the =numbers= command and its
more limited variants =numbers-up= and =numbers-down=.

#+tindex: num
#+tindex: nums
#+tindex: number
#+tindex: numbering
#+tindex: numbers
- =(numbers|nums VAR &key KEYS)= :: Iterate through numbers.  =KEYS= is one or
  several of =from=, =upfrom=, =downfrom=, =to=, =upto=, =downto=, =above=,
  =below=, =by=, and =test=.

  This command also has the aliases =num=, =number=, and =numbering=.

  The command =numbers= is used to iterate through numbers.  For example,
  =(numbers i :from 1 :to 10)= is similar to the command
  =(list i (number-sequence 1 10))=, and =(numbers i 3)= is similar to
  =(set i 3 (1+ i))=.

  In its most basic form, =numbers= iterates from a starting value to an
  inclusive ending value using the =:from= and =:to= keywords, respectively.

  #+begin_src emacs-lisp
    ;; => (1 2 3 4 5)
    (loopy (numbers i :from 1 :to 5)
           (collect i))
  #+end_src

  Unlike ~cl-loop~, =VAR= is not initialized to the starting value given.
  Instead, =VAR= is updated during the loop, like in other iteration
  commands. This avoids unexpectedly changing the value of =VAR= after the
  iteration step, as happens with some implementations of Common Lisp's ~loop~
  macro (such ~cl-loop~).

  #+begin_src emacs-lisp
    ;; => (4 (1 2 3 4))
    (loopy (list elem (list 1 2 3 4))
           (numbers num :from 1)
           (collect nums num)
           (finally-return num nums))

    ;; => (5 (1 2 3 4))
    (cl-loop for elem in (list 1 2 3 4)
             for num from 1
             collect num into nums
             finally return (list num nums))

    ;; SBCL returns 4, not 5:
    ;;
    ;; => (4 (1 2 3 4))
    (loop for elem in (list 1 2 3 4)
          for num from 1
          collect num into nums
          finally (return (list num nums)))
  #+end_src

  If the ending value is not given, then the value is incremented by 1 without
  end.

  #+begin_src emacs-lisp
    ;; => (7 8 9 10 11 12 13 14 15 16)
    (loopy (cycle 10)
           (numbers i :from 7)
           (collect i))
  #+end_src

  To specify the step size, one can use the keyword =:by=.  Except when =:test=
  is given, _the value for =:by= must be positive_.  Other keyword arguments
  (that is, =:upfrom=, =:downfrom=, =:upto=, =:downto=, =:above=, and =:below=)
  control whether the variable is incremented or decremented.

  #+begin_src emacs-lisp
    ;; => (1 3 5)
    (loopy (numbers i :from 1 :to 5 :by 2)
           (collect i))

    ;; => (7 9 11 13 15 17 19 21 23 25)
    (loopy (cycle 10)
           (numbers i :from 7 :by 2)
           (collect i))

    ;; => (1 2.5 4.0)
    (loopy (numbers i :from 1 :to 5 :by 1.5)
           (collect i))
  #+end_src

  By default, the variable's value starts at 0 and increases by 1.  To specify
  whether the value should be increasing or decreasing when using the =:by=
  keyword, one can use the keywords =:downfrom=, =:downto=, =:upfrom=, =:upto=,
  =:above=, and =:below=.  The keywords =:from= and =:to= don't by themselves
  specify a direction, and they can be used without conflict with the keyword
  arguments that do specify a direction.  Using arguments that contradict one
  another will signal an error.

  #+begin_src emacs-lisp
    ;; => (3 2 1)
    (loopy (cycle 3)
           (numbers i :downfrom 3)
           (collect i))

    ;; => (0 -1 -2 -3)
    (loopy (numbers i :downto -3)
           (collect i))

    ;; => (10 9 8 7 6 5 4 3 2)
    (loopy (numbers i :downfrom 10 :to 2)
           (collect i))

    ;; => (10 8 6 4 2)
    (loopy (numbers i :from 10 :downto 2 :by 2)
           (collect i))

    ;; => (1 2 3 4 5 6 7)
    (loopy (numbers i :from 1 :upto 7)
           (collect i))

    ;; Signals an error:
    (loopy (numbers i :downfrom 10 :upto 20)
           (collect i))
  #+end_src

  To specify an /exclusive/ ending value, use the keywords =:below= for
  increasing values and =:above= for decreasing values.

  #+begin_src emacs-lisp
    ;; => (1 2 3 4 5 6 7 8 9)
    (loopy (numbers i :from 1 :below 10)
           (collect i))

    ;; Same as above:
    (loopy (set i 1 (1+ i))
           (while (< i 10))
           (collect i))

    ;; => (10 9 8 7 6 5 4 3 2)
    (loopy (numbers i :from 10 :above 1)
           (collect i))

    ;; => (0 -1 -2)
    (loopy (numbers i :above -3)
           (collect i))
  #+end_src

  If you do not know whether you will be incrementing or decrementing, you can
  use the keyword argument =test=, whose value is a function that should return
  a non-nil value if the loop should continue, such as ~#'<=~.  The function
  receives =VAR= as the first argument and the final value as the second
  argument, as in ~(funcall TEST VAR FINAL-VAL)~.  =test= can only be used with
  =from= and =to=; it cannot be used with keywords that already describe a
  direction and an ending condition.  To match the behavior of ~cl-loop~, the
  default testing function is ~#'<=~.  When =test= is given, =by= can be
  negative.  As there is no default end value when =test= is given, =to= must
  also be given.

  #+begin_src emacs-lisp
    ;; => (10 9.5 9.0 8.5 8.0 7.5 7.0 6.5 6.0 5.5)
    (loopy (with (start 10)
                 (end 5)
                 (func #'>)
                 (step -0.5))
           (numbers i :to end :from start :by step :test func)
           (collect i))

    ;; Expands to similar code as above.
    ;; Note that with `:above', step must be positive.
    ;;
    ;; => (10 9.5 9.0 8.5 8.0 7.5 7.0 6.5 6.0 5.5)
    (loopy (with (start 10)
                 (end 5)
                 (step 0.5))
           (numbers i :from start :above end :by step)
           (collect i))

    ;; Signals an error because `:upto' implies a testing function already:
    (loopy (numbers i :from 1 :upto 10 :test #'<)
           (collect i))
  #+end_src

If you prefer using positional arguments to keyword arguments, you can use the
commands =numbers-up= and =numbers-down= to specify directions.  These commands
are simple wrappers of the above =numbers= command.

#+tindex: nums-down
#+tindex: numbers-down
#+tindex: numbering-down
- =(numbers-down|nums-down VAR START [END] &key by)= :: Equivalent to =(numbers
  VAR :from START [:downto END] &key by)=.  This command exists only for convenience.

  This command also has the aliases =numsdown= and =numbering-down=.

  #+begin_src emacs-lisp
    ;; => (10 8 6 4 2)
    (loopy (numbers-down i 10 1 :by 2)
           (collect i))

    ;; => (10 8 6 4 2)
    (loopy (numbers-down i 10 1 2)
           (collect i))
  #+end_src

#+tindex: nums-up
#+tindex: numbers-up
#+tindex: numbering-up
- =(numbers-up|nums-up VAR START [END] &key by)= :: Equivalent to =(numbers VAR
  :from START [END] &key by)=.  This command exists only for convenience.

  This command also has the aliases =numsup= and =numbering-up=.

  #+begin_src emacs-lisp
    ;; => (1 3 5 7 9)
    (loopy (numbers-up i 1 10 :by 2)
           (collect i))

    ;; => (1 3 5 7 9)
    (loopy (numbers-up i 1 10 2)
           (collect i))
  #+end_src


*** Sequence Iteration
:PROPERTIES:
:CUSTOM_ID: sequence-iteration
:DESCRIPTION: Iterating through sequences.
:END:

These commands provide various ways to iterate through sequences
([[info:elisp#Sequences Arrays Vectors]]).

#+cindex: sequence element distribution
Instead of iterating through just one sequence, the =array=, =list=, and =seq=
commands can be given multiple sequences of various sizes.  In such cases, the
elements of the sequences are {{{dfn(distributed)}}}, like in the distributive
property from mathematics.  A new sequence of distributed elements is created
before the loop runs, and that sequence is used for iteration instead of the
source sequences.  As seen in the below example, the resulting behavior is
similar to that of nested loops.

#+begin_src emacs-lisp
  ;; => ((1 3 6) (1 4 6) (1 5 6) (2 3 6) (2 4 6) (2 5 6))
  (loopy (list i '(1 2) '(3 4 5) '(6))
         (collect i))

  ;; Gives the same result as this
  (let ((result nil))
    (dolist (i '(1 2))
      (dolist (j '(3 4 5))
        (dolist (k '(6))
          (push (list i j k) result))))
    (nreverse result))

  ;; and this
  (cl-loop for i in '(1 2)
           append (cl-loop for j in '(3 4 5)
                           append (cl-loop for k in '(6)
                                           collect (list i j k))))
#+end_src

The =array= and =sequence= commands can use the same keywords as the =numbers=
command ([[#numeric-iteration]]) for working with the index and choosing a range of
the sequence's elements through which to iterate.  In addition to those
keywords, they also have an =index= keyword, which names the variable used to
store the accessed index during the loop.

#+begin_src emacs-lisp
  ;; => ((1 . 9) (3 . 6) (5 . 5) (7 . 3) (9 . 1))
  (loopy (array i [10 9 8 6 7 5 4 3 2 1] :from 1 :by 2 :index ind)
         (collect (cons ind i)))
#+end_src

Keep in mind that if used with sequence distribution, these keywords affect
iterating through the sequence of distributed elements.  That is, they do not
affect how said sequence is produced.  In the example below, see that ~cddr~ is
applied to the sequence of distributed elements.  It is /not/ applied to the
source sequences.

#+begin_src emacs-lisp
  ;; This code creates the sequence of distributed elements
  ;; ((1 4) (1 5) (1 6) (2 4) (2 5) (2 6) (3 4) (3 5) (3 6))
  ;; and then moves through this sequence using `cddr'.
  ;;
  ;; => ((1 4) (1 6) (2 5) (3 4) (3 6))
  (loopy (list i '(1 2 3) '(4 5 6) :by #'cddr)
         (collect i))

  ;; Not the same as:
  ;; => ((1 4) (1 6) (3 4) (3 6))
  (loopy (list i '(1 3) '(4 6))
         (collect i))
#+end_src


#+tindex: array
#+tindex: string
#+tindex: arraying
#+tindex: stringing
- =(array|string VAR EXPR [EXPRS] &key KEYS)= :: Loop through the
  elements of the array =EXPR=.  In Emacs Lisp, strings are arrays whose
  elements are characters.

  This command also has the aliases =arraying= and =stringing=.

  =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=,
  =downto=, =above=, =below=, =by=, =test=, and =index=.  =index= names the
  variable used to store the index being accessed.  For the others, see the
  =numbers= command.

  If multiple arrays are given, then the elements of these arrays are
  distributed into an array of lists.  In that case, the above keywords apply to
  this new, resulting array of lists.

  #+BEGIN_SRC emacs-lisp
    (loopy (array i [1 2 3])
           (do (message "%d" i)))

    ;; => (1 3)
    (loopy (array i [1 2 3 4] :by 2)
           (collect i))

    ;; Collects the integer values representing each character.
    ;; => (97 98 99)
    (loopy (string c "abc")
           (collect c))

    ;; This is the same as using [(1 3) (1 4) (2 3) (2 4)].
    ;; => ((1 3) (1 4) (2 3) (2 4))
    (loopy (array i [1 2] [3 4])
           (collect i))

    ;; => ((1 3) (2 3))
    (loopy (array i [1 2] [3 4] :by 2)
           (collect i))
  #+END_SRC

#+tindex: cons
#+tindex: conses
#+tindex: consing
- =(cons|conses VAR EXPR &key by)= :: Loop through the cons cells of =EXPR=.
  Optionally, find the cons cells via the function =by= instead of =cdr=.

  This command also has the alias =consing=.

  #+BEGIN_SRC emacs-lisp
    ;; => ((1 2 3) (2 3) (3))
    (loopy (cons i '(1 2 3))
           (collect coll i)
           (finally-return coll))

    ;; => ((1 2 3 4 5 6) (3 4 5 6) (5 6))
    (loopy (cons i '(1 2 3 4 5 6) :by #'cddr)
           (collect coll i)
           (finally-return coll))
  #+END_SRC

#+tindex: list
#+tindex: listing
#+tindex: each
- =(list|each VAR EXPR [EXPRS] &key by)= :: Loop through each element of the
  list =EXPR=.  Optionally, move through the list using =by= instead of =cdr=.

  This command also has the alias =listing=.

  If multiple lists are given, distribute the elements of the lists into one new
  list.  In such cases, =by= applies to the new list, not the arguments of the
  command.

  #+BEGIN_SRC emacs-lisp
    ;; => (1 4 7 10).
    (loopy (list i (number-sequence 1 10 3))
           (collect i))

    ;; => (1 3 5)
    (loopy (list i '(1 2 3 4 5 6) :by #'cddr)
           (collect i))

    ;; => ((1 4) (1 5) (1 6) (2 4) (2 5) (2 6) (3 4) (3 5) (3 6))
    (loopy (list i '(1 2 3) '(4 5 6))
           (collect i))

    ;; => ((1 4) (1 6) (2 5) (3 4) (3 6))
    (loopy (list i '(1 2 3) '(4 5 6) :by #'cddr)
           (collect i))
  #+END_SRC

#+tindex: map
#+tindex: map-pairs
#+tindex: mapping
#+tindex: mapping-pairs
- =(map|map-pairs VAR EXPR &key unique)= :: Iterate through the dotted key-value
  pairs of map =EXPR=, using the function ~map-pairs~ from the =map.el= library.
  This library generalizes working with association lists ("alists"), property
  lists ("plists"), hash tables, and vectors.

  This command also has the aliases =mapping= and =mapping-pairs=.

  In each dotted pair assigned to =VAR=, the ~car~ is the key and the ~cdr~ is
  the value.

  By default, only the unique keys are used.  To disable this deduplication,
  pass ~nil~ to the =unique= keyword argument.

  In general, as a map in not necessarily a sequence, you should not rely on the
  order in which the key-value pairs are found.  There is no guarantee that they
  be in the same order each time.

  These pairs are created before the loop begins via ~map-pairs~.  In other
  words, the map =EXPR= is not processed progressively, but all at once.
  Therefore, this command can have a noticeable start-up cost when working with
  very large maps.

  #+begin_src emacs-lisp
    ;; => ((a . 1) (b . 2))
    (loopy (map pair '((a . 1) (b . 2)))
           (collect pair))

    ;; => ((a b) (1 2))
    (loopy (map (key . value) '((a . 1) (b . 2)))
           (collect keys key)
           (collect values value)
           (finally-return keys values))

    ;; => ((:a :b) (1 2))
    (loopy (map (key . value) '(:a 1 :b 2))
           (collect keys key)
           (collect values value)
           (finally-return keys values))

    ;; NOTE: For vectors, the keys are indices.
    ;; => ((0 1) (1 2))
    (loopy (map (key . value) [1 2])
           (collect keys key)
           (collect values value)
           (finally-return keys values))

    ;; => ((a b) (1 2))
    (let ((my-table (make-hash-table)))
      (puthash 'a 1 my-table)
      (puthash 'b 2 my-table)

      (loopy (map (key . value) my-table)
             (collect keys key)
             (collect values value)
             (finally-return keys values)))
  #+end_src

  Depending on how a map is created, a map might contain a key multiple times.
  Currently, the function ~map-pairs~ returns such keys.  However, by default,
  the ~loopy~ command =map-pairs= ignores such duplicate keys.  This is for two
  reasons:
  1. This is more consistent with the command =map-ref=, for which such
     duplicates are more likely to cause errors.
  2. For maps that can have duplicate keys (such as alists and plists), there
     are already other iteration commands (such as =list= and =cons=) that
     explicitly include the duplicates.

  Again, this can be disabled by setting =unique= to nil.

  #+begin_src emacs-lisp
    ;; A comparison of setting the `unique' key to nil:
    ;;
    ;; => ((a 1) (a 2) (b 3))
    (loopy (map (key . val) '((a . 1) (a . 2) (b . 3)) :unique nil)
           (collect (list key val)))

    ;; In this case, `list' has the same result:
    ;; => ((a 1) (a 2) (b 3))
    (loopy (list (key . val) '((a . 1) (a . 2) (b . 3)))
           (collect (list key val)))

    ;; => ((:a 1) (:a 2) (:b 3))
    (loopy (map (key . val) '(:a 1 :a 2 :b 3) :unique nil)
           (collect (list key val)))

    ;; In this case, `cons' has the same result:
    ;; => ((:a 1) (:a 2) (:b 3))
    (loopy (cons (key val) '(:a 1 :a 2 :b 3) :by #'cddr)
           (collect (list key val)))
  #+end_src

#+tindex: sequence
#+tindex: sequencing
- =(sequence VAR EXPR [EXPRS] &key KEYS)= :: Loop through the sequence
  =EXPR=, binding =VAR= to the elements of the sequence (a list or an array).
  Because it is more generic, =sequence= is somewhat less efficient than the
  =list= and =array= commands.

  #+ATTR_TEXINFO: :tag Note
  #+begin_quote
  For more on sequences, see [[info:elisp#Sequences Arrays Vectors]].  This command
  works with the basic sequences understood by the Emacs Lisp functions ~length~
  and ~elt~.  It does not work with the generic sequences understood by the
  library =seq.el=.  For those, use the =seq= command.
  #+end_quote

  This command also has the alias =sequencing=.

  =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=,
  =downto=, =above=, =below=, =by=, =test=, and =index=.  =index= names the
  variable used to store the index being accessed.  For the others, see the
  =numbers= command.

  #+ATTR_TEXINFO: :tag Warning
  #+begin_quote
  Array elements can be accessed in constant time, but not list elements.  For
  lists, the =sequence= command is fastest when moving forwards through the
  list.  In that case, the command does not have to search from the beginning of
  the list each time to find the next element.  The =sequence= command can be
  noticeably slower for lists when working backwards or when the =test=
  parameter (for which direction cannot be assumed) is provided.
  #+end_quote

  If multiple sequences are given, then these keyword arguments apply to the
  resulting sequence of distributed elements.

  #+BEGIN_SRC emacs-lisp
    ;; => (1 2 3)
    (loopy (sequence i [1 2 3])
           (collect coll i)
           (finally-return coll))

    ;; => (0 2 4)
    (loopy (sequence i [0 1 2 3 4 5] :by 2)
           (collect i))

    ;; => (1 3 5)
    (loopy (sequence i [0 1 2 3 4 5 6]
                :by 2 :from 1 :to 5)
           (collect i))

    ;; => (5 3 1)
    (loopy (sequence i '(0 1 2 3 4 5 6)
                :downfrom 5 :by 2 :to 1)
           (collect i))

    ;; => ((1 3) (1 4) (2 3) (2 4))
    (loopy (sequence i [1 2] '(3 4))
           (collect i))

    ;; => ((1 3) (2 3))
    (loopy (sequence i [1 2] '(3 4) :by 2)
           (collect i))
  #+END_SRC

#+tindex: seq
#+tindex: seqing
- =(seq VAR EXPR [EXPRS] &key KEYS)= :: For a generic sequence which implements
  the features of the library =seq.el=, loop through the generic sequence
  =EXPR=, binding =VAR= to the elements of the sequence.  Because it is more
  generic, =seq= can be slower than the =sequence= command, which in turn is
  somewhat less efficient than the =list= and =array= commands.

  If multiple generic sequences are given, then these keyword arguments apply to
  the resulting generic sequence of distributed elements.

  =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=,
  =downto=, =above=, =below=, =by=, =test=, and =index=.  =index= names the
  variable used to store the index being accessed.  For the others, see the
  =numbers= command.

  This command also has the alias =seqing=.

  The =seq= command naively loops through the generic sequence using ~seq-elt~
  and ~seq-length~.  Because other packages might implement custom sequences
  using lists, no special consideration is made for optimizing the =seq= command
  when given a list.

  Because the =seq= command currently uses the function ~seq-length~ to detect
  when to leave the loop, it does not work with infinite sequences.  For
  infinite sequences, consider using the =stream= command.

  #+begin_src emacs-lisp
    ;; => (0 2 4)
    (loopy (seq i [0 1 2 3 4 5] :by 2)
           (collect i))

    ;; => (5 3 1)
    (loopy (seq i '(0 1 2 3 4 5 6)
                :downfrom 5 :by 2 :to 1)
           (collect i))

    ;; => ((1 3) (2 3))
    (loopy (seq i [1 2] '(3 4) :by 2)
           (collect i))
  #+end_src

#+tindex: stream
#+tindex: streaming
- =(stream VAR EXPR &key by)= :: Iterate through the elements for the stream
  =EXPR=.  If =by= is non-nil (default: 1), then move to the next n-th element
  during each iteration.  This command is a special case of the =substream=
  command (described below), setting =VAR= to the first element of each
  substream.  For more information on streams, see the command =substream=.

  This command also has the alias =streaming=.

  #+begin_src emacs-lisp
    ;; => (0 1 2)
    (loopy (stream i (stream [0 1 2]))
           (collect i))

    ;; Same as the above:
    ;; => (0 1 2)
    (loopy (substream i (stream [0 1 2]))
           (collect (stream-first i)))
  #+end_src

#+tindex: substream
#+tindex: substreaming
- =(substream VAR EXPR &key by length)= :: Iterate through the sub-streams of
  stream =EXPR=, similar to the command =cons=.  If =by= is non-nil (default:
  1), then move to the next n-th substream during each iteration.  If =length=
  is given, then the substream bound to =VAR= is only the specified length.

  This command operates on the =stream= type defined by the library =stream=
  [[https://elpa.gnu.org/packages/stream.html][from GNU ELPA]], which is not to be confused with the Emacs Lisp "input streams"
  and "output streams" used for reading and printing text ([[info:elisp#Read and
  Print]]).  The "streams" defined by the =stream= library are like lazy sequences
  and are compatible with features from the built-in =seq= library, such as
  ~seq-elt~ and ~seq-do~.

  Sub-streams can only be destructured using the =&seq= feature of the default
  destructuring method ([[#basic-destructuring][Basic Destructuring]]), or by using the =seq= flag
  ([[#flags][Using Flags]]).  Streams are neither lists nor arrays.

  This command also has the alias =substreaming=.

  #+begin_src emacs-lisp
    (require 'stream)

    ;; => (0 1 2)
    (loopy (substream i (stream [0 1 2]))
           (collect (stream-first i)))

    ;; => ((0 1 2)
    ;;     (1 2 nil)
    ;;     (2 nil nil))
    (loopy (substream [&seq i j k] (stream [0 1 2]))
           (collect (list i j k)))

    ;; => ((0 1)
    ;;     (1 2)
    ;;     (2 3)
    ;;     (3 nil))
    (loopy (flag seq)
           ;; Using the `seq.el' library to destructure,
           ;; not destructuring as a list:
           (substream (i j) (stream '(0 1 2 3)))
           (collect (list i j)))

    ;; => ((0 1 2 3 4 5)
    ;;     (2 3 4 5)
    ;;     (4 5))
    (loopy (substream i (stream [0 1 2 3 4 5]) :by 2)
           (set inner-result nil)
           (do (seq-do (lambda (x) (push x inner-result))
                       i))
           (collect (reverse inner-result)))

    ;; => ((0 1)
    ;;     (2 3)
    ;;     (4 5))
    (loopy (set inner-result nil)
           ;; Using `:length' limits the length of the substream
           ;; bound to `i'.
           (substream i (stream [0 1 2 3 4 5]) :by 2 :length 2)
           (do (seq-do (lambda (x) (push x inner-result))
                       i))
           (collect (reverse inner-result)))
  #+end_src

*** Sequence Index Iteration
:PROPERTIES:
:CUSTOM_ID: sequence-index-iteration
:DESCRIPTION: Iterating through indices without accessing values.
:END:

This command is for iterating through a sequence's indices without accessing
the actual values of that sequence.  This is helpful if you know ahead of
time that you are only interested in a small subset of the sequence's
elements.

As with the =array= and =seq= commands, the =seq-index= command can use the
same keywords as the =numbers= command ([[#numeric-iteration]]) for working with
the index and choosing a range of the sequence elements through which to
iterate.

#+tindex: sequence-index
#+tindex: sequencing-index
#+tindex: seq-index
#+tindex: seqing-index
#+tindex: array-index
#+tindex: arraying-index
#+tindex: list-index
#+tindex: listing-index
#+tindex: string-index
#+tindex: stringing-index
- =(sequence-index VAR EXPR &key KEYS)= :: Iterate through the indices of =EXPR=.

  There is only one implementation of this command; there are no
  type-specific versions.  This command also has the following aliases:
  - =array-index=, =arraying-index=
  - =list-index=, =listing-index=
  - =string-index=, =stringing-index=
  - =sequencing-index=
  -  =seq-index=, =seqing-index=


  =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=,
  =downto=, =above=, =below=, =by=, and =test=.  For their meaning, see the
  =numbers= command.  This command is very similar to =numbers=, except that it
  can automatically end the loop when the index of the final element is reached.
  With =numbers=, one would first need to explicitly calculate the length of the
  sequence.

  #+begin_src emacs-lisp
    ;; => (97 98 99 100 101 102)
    (loopy (with (my-string "abcdef"))
           (string-index idx my-string)
           (collect (aref my-string idx)))

    ;; Works the same as
    (loopy (with (my-string "abcdef"))
           (numbers idx :from 0 :below (length my-string))
           (collect (aref my-string idx)))
  #+end_src

  This command does not support destructuring.

  #+begin_src emacs-lisp
    ;; => (0 1 2)
    (loopy (sequence-index i [1 2 3])
           (collect i))

    ;; => (0 1 2)
    (loopy (array-index i "abc")
           (collect i))

    ;; => (0 1 2)
    (loopy (list-index i '(1 2 3))
           (collect i))

    ;; => (8 6 4 2)
    (loopy (with (my-seq [0 1 2 3 4 5 6 7 8 9 10]))
           (sequence-index idx my-seq :from 8 :downto 1 :by 2)
           (collect (elt my-seq idx)))
  #+end_src

*** Sequence Reference Iteration
:PROPERTIES:
:CUSTOM_ID: sequence-reference-iteration
:DESCRIPTION: Iterating through places/fields in sequences.
:END:

These commands all iterate through ~setf~-able places as generalized
variables ([[info:elisp#Generalized Variables]]).  These generalized variables
are commonly called "references", "fields", or "places".  The below example
demonstrates using ~(nth 1 my-list)~ and ~(aref my-array 1)~ as ~setf~-able
places.

#+begin_src emacs-lisp
  ;; => (1 99 3 4 5)
  (let ((my-list '(1 2 3 4 5)))
    (setf (nth 1 my-list) 99)
    my-list)

  ;; => [(1 2 3) (4 . 99)]
  (let ((my-array [(1 2 3) (4 5 6)]))
    (setf (cdr (aref my-array 1)) 99)
    my-array)
#+end_src

Like other commands, "field" or "reference" commands can also use
destructuring, in which case the fields/places of the sequence are
destructured into "sub-fields", like the ~cdr~ of the second array element
in the example above.

#+attr_texinfo: :tag Caution
#+begin_quote
Be aware that using ~setf~ on an array sub-sequence named by =&rest=
will only overwrite values, not truncate or grow the array. 
#+end_quote

#+attr_texinfo: :tag Warning
#+begin_quote
Unfortunately, not all kinds of recursive destructuring work on references.

Currently:
- =&optional= variables are not supported
- =SUPPLIED= variables are not supported for =&key= and =&map=.
- Non-nil default values for =&optional=, =&key=, and =&map= are not supported.
#+end_quote

As with the =array= and =seq= commands, the =array-ref= and =seq-ref=
commands can use the same keywords as the =numbers= command
([[#numeric-iteration]]) for working with the index and choosing a range of the
sequence elements through which to iterate.  In addition to those keywords,
they also have an =index= keyword, which names the variable used to store
the accessed index during the loop.

#+tindex: array-ref
#+tindex: string-ref
#+tindex: arraying-ref
#+tindex: stringing-ref
- =(array-ref|string-ref VAR EXPR &key KEYS)= :: Loop through the elements of
  the array =EXPR=, binding =VAR= as a ~setf~-able place.

  This command also has the aliases =arraying-ref= and =stringing-ref=.

  =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=,
  =downto=, =above=, =below=, =by=, and =index=.  =index= names the variable
  used to store the index being accessed.  For others, see the =numbers=
  command.

  #+BEGIN_SRC emacs-lisp
    ;; => "aaa"
    (loopy (with (my-str "cat"))
           (array-ref i my-str)
           (do (setf i ?a))
           (finally-return my-str))

    ;; => "0a2a4a6a89"
    (loopy (with (my-str "0123456789"))
           (array-ref i my-str :from 1 :by 2 :to 7)
           (do (setf i ?a))
           (finally-return my-str))

    ;; Works the same as
    (loopy (with (my-str "0123456789"))
           (numbers idx 1 7 :by 2)
           (do (setf (aref my-str idx) ?a))
           (finally-return my-str))
  #+END_SRC

#+tindex: list-ref
#+tindex: listing-ref
- =(list-ref VAR EXPR &key by)= :: Loop through the elements of the list =EXPR=,
  binding =VAR= as a ~setf~-able place.  Optionally, update the list via
  function =by= instead of ~cdr~.

  This command also has the aliases =listing-ref=.

  #+BEGIN_SRC emacs-lisp
    ;; => (7 7 7)
    (loopy (with (my-list '(1 2 3)))
           (list-ref i my-list)
           (do (setf i 7))
           (finally-return my-list))

    ;; Works similar to
    (loopy (with (my-list '(1 2 3)))
           (numbers idx :below (length my-list))
           (do (setf (nth idx my-list) 7))
           (finally-return my-list))

    ;; => (7 2 7)
    (loopy (with (my-list '(1 2 3)))
           (list-ref i my-list :by #'cddr)
           (do (setf i 7))
           (finally-return my-list))

    ;; => ([1 7] [2 7])
    (loopy (with (my-list '([1 2] [2 3])))
           (list-ref [_ i] my-list)
           (do (setf i 7))
           (finally-return my-list))
  #+END_SRC

#+tindex: map-ref
#+tindex: mapping-ref
- =(map-ref VAR EXPR &key key unique)= :: Loop through the values of map =EXPR=,
  binding =VAR= as a ~setf~-able place.  Like the command =map=, this command
  uses the =map.el= library.

  This command also has the alias =mapping-ref=.

  =key= is a variable in which to store the current key for the ~setf~-able
  place referred to by =VAR=.  This is similar to the =index= keyword parameter
  of other commands.  This is not the same as the =key= keyword parameter of the
  accumulation commands.

  Like in the command =map=, the keys of the map are generated via the function
  ~map-keys~ before the loop is run, which can be expensive for large maps.

  Similar to =map=, any duplicate keys are ignored by default.  This can be
  disabled by setting the =unique= keyword argument to nil, though note that
  using such duplicate keys will still refer to the value of the first
  occurence.  There is no way to use a duplicate key to refer to the
  duplicate's value.

  #+begin_src emacs-lisp
    ;; Duplicate keys are ignored by default.
    ;;
    ;; => (:a 8 :a 'ignored :b 10)
    (loopy (with (map (list :a 1 :a 'ignored :b 3)))
           (map-ref i map)
           (do (cl-incf i 7))
           (finally-return map))

    ;; If duplicates are not ignored:
    ;;
    ;; => (:a 15 :a ignored :b 10)
    (loopy (with (map (list :a 1 :a 'ignored :b 3)))
           (map-ref i map :unique nil)
           (do (cl-incf i 7))
           (finally-return map))

    ;; Getting the key using `key':
    ;;
    ;; => (((cat . 7)       ; The map itself
    ;;      (dog . 7)
    ;;      (zebra . 7))
    ;;     (cat dog zebra)) ; The keys
    (loopy (with (map (list (cons 'cat 1)
                            (cons 'dog 2)
                            (cons 'zebra 3))))
           (map-ref i map :key my-key)
           (do (setf i 7))
           (collect my-key)
           (finally-return map loopy-result))
  #+end_src

#+tindex: sequence-ref
#+tindex: sequencing-ref
- =(sequence-ref VAR EXPR &key KEYS)= :: Loop through the elements of the
  sequence =EXPR= (an array or list), binding =VAR= as a ~setf~-able place.

  This command also has the aliases =sequencing-ref=.

  =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=,
  =downto=, =above=, =below=, =by=, =test=, and =index=.  =index= names the
  variable used to store the index being accessed.  For others, see the
  =numbers= command.

  #+BEGIN_SRC emacs-lisp
    ;; => (7 7 7 7)
    (loopy (with (my-seq (list 1 2 3 4)))
           (sequence-ref i my-seq)
           (do (setf i 7))
           (finally-return my-seq))

    ;; => (0 cat 2 cat 4 cat 6 cat 8 cat)
    (loopy (with (my-list (list 0 1 2 3 4 5 6 7 8 9)))
           (sequence-ref i my-list :from 1 :by 2 )
           (do (setf i 'cat))
           (finally-return my-list))

    ;; => "0123456a8a"
    (loopy (with (my-str (copy-sequence "0123456789")))
           (sequence-ref i my-str :downto 6 :by 2 )
           (do (setf i ?a))
           (finally-return my-str))
  #+END_SRC

#+tindex: seq-ref
#+tindex: seqing-ref
- =(seq-ref VAR EXPR &key KEYS)= :: Loop through the elements of the generic
  sequence =EXPR=, via the features of the library =seq.el=, binding =VAR= as a
  ~setf~-able place.

  #+attr_texinfo: :tag Note
  #+begin_quote
    Not all generic sequences are mutable, so not all generic sequences work as
    a ~setf~-able place.
  #+end_quote

  =KEYS= is one or several of =from=, =upfrom=, =downfrom=, =to=, =upto=,
  =downto=, =above=, =below=, =by=, =test=, and =index=.  =index= names the
  variable used to store the index being accessed.  For others, see the
  =numbers= command.

  This command also has the alias =seqing-ref=.

  The =seq-ref= command naively loops through the generic sequence using
  ~seq-elt~ and ~seq-length~.  Because other packages might implement custom
  sequences using lists, no special consideration is made for optimizing the
  =seq-ref= command when given a list.

  Because the =seq-ref= command currently uses the function ~seq-length~ to
  detect when to leave the loop, it does not work with infinite sequences.

  #+BEGIN_SRC emacs-lisp
    ;; => (7 7 7 7)
    (loopy (with (my-seq (list 1 2 3 4)))
           (seq-ref i my-seq)
           (do (setf i 7))
           (finally-return my-seq))

    ;; => (0 cat 2 cat 4 cat 6 cat 8 cat)
    (loopy (with (my-list (list 0 1 2 3 4 5 6 7 8 9)))
           (seq-ref i my-list :from 1 :by 2 )
           (do (setf i 'cat))
           (finally-return my-list))

    ;; => "0123456a8a"
    (loopy (with (my-str (copy-sequence "0123456789")))
           (seq-ref i my-str :downto 6 :by 2 )
           (do (setf i ?a))
           (finally-return my-str))
  #+END_SRC

** Accumulation
:PROPERTIES:
:CUSTOM_ID: accumulation-commands
:DESCRIPTION: Accumulating values into new sequences, aggregating values, etc.
:END:

{{{dfn(Accumulation commands)}}} are used to accumulate or aggregate values into
a variable, such as creating a list of return values or summing the elements of
a sequence.

Unlike iteration commands, you can refer to the same accumulation variable in
multiple accumulation commands if needed.

#+begin_src emacs-lisp
  ;; => (1 6 2 7 3 8)
  (loopy (list i '(1 2 3))
         (collect coll i)
         (collect coll (+ i 5))
         (finally-return coll))
#+end_src

#+attr_texinfo: :tag Note
#+begin_quote
Keep in mind that it is an error to modify accumulation variables outside of
accumulation commands.  This restriction allows accumulations to be much faster.
#+end_quote

#+cindex: accumulation destructuring
Similar to iteration commands, accumulation commands can also use destructuring.
In accumulation commands, the values resulting from destructuring are
accumulated, instead of the destructured value.

#+begin_src emacs-lisp
  ;; => ((1 4) (2 5) (3 6))
  (loopy (list elem '((1 2 3) (4 5 6)))
         (collect (coll1 coll2 coll3) elem)
         (finally-return coll1 coll2 coll3))

  ;; => (5 7 9)
  (loopy (list elem '((1 2 3) (4 5 6)))
         (sum (sum1 sum2 sum3) elem)
         (finally-return sum1 sum2 sum3))

  ;; Returns the same values as above.
  (loopy (list elem '((1 2 3) (4 5 6)))
         (set sum1 (cl-first elem)  (+ sum1 (cl-first elem)))
         (set sum2 (cl-second elem) (+ sum2 (cl-second elem)))
         (set sum3 (cl-third elem)  (+ sum3 (cl-third elem)))
         (finally-return sum1 sum2 sum3))
#+end_src

#+cindex: implied accumulation results
Like in ~cl-loop~, you do not need to supply a variable name to accumulation
commands.  If no accumulation variable is given and the =finally-do= special
macro argument is not used, then the accumulated value is understood to be the
return value of the loop.  Such a return value is described in this document as
an {{{dfn(implied return value)}}} or an {{{dfn(implicit return value)}}}.  An
implied return value can be overridden by using the the =return= and
=return-from= loop commands or the =finally-return= macro argument.

#+begin_src emacs-lisp
  ;; => (1 2 3)
  (cl-loop for i from 1 to 3
           collect i)

  ;; => (1 2 3)
  (loopy (numbers i :from 1 :to 3)
         (collect i))
#+end_src

#+vindex: loopy-result
Unlike ~cl-loop~, Loopy uses a default accumulation variable, which is named
~loopy-result~.  When used this way, ~loopy-result~ is described as an
{{{dfn(implied accumulation variable)}}} or an {{{dfn(implicit accumulation
variable)}}}.  This variable can be used in the =after-do=, =finally-do=, and
=finally-return= special macro arguments.

#+begin_src emacs-lisp
  ;; => (0 1 2 3 4 5)
  (loopy (numbers i :from 1 :to 10)
         (when (> i 5) (leave))
         (collect i)
         (finally-return (cons 0 loopy-result)))

  ;; => (0 1 2 3)
  (loopy (list i '(1 2 3))
         (collect i)
         (after-do (push 0 loopy-result)))

  ;; => (0 1 2 3)
  (loopy (list i '(1 2 3))
         (collect i)
         (finally-do (push 0 loopy-result)))
  #+end_src

Like in ~cl-loop~, when multiple accumulation commands do not name an
accumulation variable, they will all use the same implied accumulation variable
(~loopy-result~).  As a consequence, one must explicitly name separate
accumulation variables to be able to accumulate into separate values.  This can
make accumulation slower, because ~loopy~ ensures that named accumulation
variables (excluding the previously mentioned ~loopy-result~) have the correct
value during the loop.  For example, ~loopy~ will construct named accumulation
variables containing lists in the correct order, instead of using the more
efficient ~push~-~nreverse~ idiom.  This behavior can be disabled by optimizing
accumulation variables using the =accum-opt= special macro argument
([[#optimized-accums]]).

#+begin_src emacs-lisp
  ;; Both command usages of `collect' and `append'
  ;; use `loopy-result' as the implied accumulation variable:
  ;;
  ;; => (1 11 2 12 3 13)
  (loopy (list i '(1 2 3))
         (collect i)
         (append (list (+ 10 i)) :at end)
         (finally-return loopy-result))
#+end_src

In general, you should not attempt to modify or use the value of optimized
accumulation variables during the loop, including ~loopy-result~ when it is used
as the implied accumulation variable for a command, because they are not
guaranteed to have a correct value when efficiently building sequences
([[#optimized-accums]]).  For some commands, such as those listed in [[#accum-numeric]]
and [[#accum-generic]], this does not matter.

#+begin_src emacs-lisp
  ;; See that `loopy-result' is not in the correct order during the loop
  ;; (the exact value of an optimized accumulation variable during the loop
  ;; is an implementation detail):
  ;;
  ;; => (6 5 4 3 2 1)
  (loopy (number i :from 1 :to 10)
         (collect i)
         (when (> i 5)
           (return loopy-result)))
#+end_src

Implied results are only required to be correct after the loop ends (before code
in =else-do= is run).  Furthermore, because using a =return= or =return-from=
command overrides implied return values, using these commands can prevent
implied accumulation results from being finalized.  Using the =leave= command,
which exits the loop without returning a value, does not affect the correctness
of implied results.

#+begin_src emacs-lisp
  ;; See that the `leave' command does not prevent finalizing
  ;; optimized accumulation variables, unlike the above `return' command:
  ;;
  ;; => (1 2 3 4 5 6)
  (loopy (number i :from 1 :to 10)
         (collect i)
         (when (> i 5) (leave)))
#+end_src

Be aware that explicitly named accumulation variables do not affect the implied
return value of a loop.  Such values must be returned explicitly, or they will
be ignored when the macro returns a value.  This limitation is needed for more
consistently handling the complexity that comes from allowing unknown kinds of
destructuring via the alternative destructuring systems, such as the possibly
intermediate variables that can be generated by ~pcase~ patterns.  This may
change in the future.

#+begin_src emacs-lisp
  ;; See how the variable `my-explicit-variable' is ignored when
  ;; returning a final value:
  ;;
  ;; => (1 2 3)
  (loopy (list i '(1 2 3))
         (collect i)
         (collect my-explicit-variable (* 2 i)))
#+end_src

Therefore, when mixing implicit and explicit accumulation variables, you must
always use the =finally-return= special macro argument to return all of the
accumulation results.

#+begin_src emacs-lisp
  ;; => ((1 2 3)  ; `loopy-result'
  ;;     (2 4 6)  ; `my-other-collection'
  ;;     (1 2 3)  ; `car-coll'
  ;;     (2 4 6)) ; `cdr-coll'
  (loopy (list i '(1 2 3))
         (collect i) ; Uses `loopy-result'
         (set j (* 2 i))
         (collect my-other-collection j)
         (collect (car-coll . cdr-coll) (cons i j))
         (finally-return loopy-result
                         my-other-collection
                         car-coll
                         cdr-coll))
#+end_src

#+cindex: accumulation compatibility
For all accumulation variables used by multiple accumulation commands, you
should make sure that the commands are actually compatible.  If not, then
~loopy~ will signal an error.  For example, you should not try to accumulate
=collect= results and =sum= results into the same variable, as you cannot use a
list as a number.  On the other hand, =sum= and =multiply= are compatible, since
they both act on numbers.

#+begin_src emacs-lisp
  ;; Incompatible commands:
  ;; => ERROR
  (loopy (numbers i :from 1 :to 3)
         (collect i)
         (sum i))

  ;; Compatible commands:
  ;; => 27
  (loopy (with (loopy-result 0))
         (numbers i :from 1 :to 3)
         (sum i)
         (multiply i))
#+end_src

#+cindex: accumulation initial values
In the same general area as accumulation command compatability, you should keep
in mind that each accumulation command has a default initialization value for
the accumulation variable.  For most commands, this is ~nil~.  This
documentation tries to note when it is not ~nil~.  For example, the default
starting value for the =sum= command is ~0~ and the default starting value for
the =multiply= command is ~1~.  The default initialization value used by an
accumulation command can be overridden using the =with= special macro argument.
If two commands use different initial values and the =with= special
macro argument is not used, an error is signaled.

#+attr_texinfo: :tag Warning
#+begin_quote
Currently, a warning is raised when the default initial values of accumulation
commands conflict.  In the future, this will be an error.  To resolve this
conflict, use the =with= special macro argument, as noted above.
#+end_quote

#+begin_src emacs-lisp
  ;; Signals an error:
  ;;
  (loopy (numbers i :from 1 :to 3)
         (sum my-accum i) ; Defaults to 0.
         (multiply my-accum i) ; Defaults to 1.
         (finally-return my-accum))

  ;; No warning because using `with':
  ;;
  ;; => 87
  (loopy (with (my-accum 10))
         (numbers i :from 1 :to 3)
         (sum my-accum i) ; Default not used.
         (multiply my-accum i) ; Default not used.
         (finally-return my-accum))
#+end_src

*** Common Properties and Arguments of Accumulation Commands
:PROPERTIES:
:END:

You will notice that each accumulation command has an alias of the command name
in the present participle form (the "-ing" form).  For example, instead of
"minimize", you can use "minimizing".  Instead of "sum" and "append", you can
use "summing" and "appending".  This is similar to the behavior of ~cl-loop~,
and helps to avoid name collisions when using the ~loopy-iter~ macro
([[#loopy-iter][The ~loopy-iter~ Macro]]).

#+cindex: accumulation keyword arguments
Some accumulation commands have optional keyword parameters, which are listed
in the command's definition.  To avoid repetition, the common parameters are
all described below.

#+cindex: accumulation keyword at
- =at= :: Where to place a value.  One of =end=, =start=, or =beginning=
  (equivalent to =start=).  If ungiven, defaults to =end=.  These positions
  need not be quoted.

  #+begin_src emacs-lisp
    ;; => (1 2 3)
    (loopy (list i '(1 2 3))
           (collect i :at end))

    ;; => (3 2 1)
    (loopy (list i '(1 2 3))
           (collect i :at start))
  #+end_src

#+cindex: accumulation keyword into
- =into= :: An alternative way to specify the variable into which to
  accumulate values.  One would normally just give =VAR= as the first
  argument of the loop command, but if you wish, you can use this keyword
  argument for a syntax more like ~cl-loop~.

  As all accumulation commands support this keyword, it is not listed in
  any command definition.

  #+begin_src emacs-lisp
    ;; => (1 2 3)
    (loopy (list i '(1 2 3))
           (collect my-collection i)
           (finally-return my-collection))

    ;; => (1 2 3)
    (loopy (list i '(1 2 3))
           (collect i :into my-collection)
           (finally-return my-collection))
  #+end_src

#+cindex: accumulation keyword test
- =test= :: A function of two arguments, usually used to test for equality.
  This function is normally used to test if a value is already present in the
  accumulating sequence. If so, the function should return a non-nil value.

  #+attr_texinfo: :tag Note
  #+begin_quote
  This argument is similar to the =:test= argument used by =cl-lib=, but is
  closer to the optional =testfn= argument used by =seq= (for example, in
  ~seq-contains-p~).  There are two important differences:
  1. The default test function is ~equal~, like in other Emacs Lisp libraries,
     not ~eql~.
  2. The first argument is the existing value or sequence and the second
     argument is the tested value.  This is the /opposite/ of the order used by
     ~cl-member~ and ~memq~.
  #+end_quote

  #+begin_src emacs-lisp
    ;; Only add items to the list whose `car's are not already present
    ;; or whose `cdr' is not 3:
    ;;
    ;; => ((a . 1) (c . 4))
    (loopy (with (test-fn (lambda (seq-val new-val)
                            (or (equal (cdr new-val)
                                       3)
                                (eq (car seq-val)
                                    (car new-val))))))
           (list i '((a . 1) (a . 2) (b . 3) (c . 4)))
           (adjoin i :test test-fn))
  #+end_src

#+cindex: accumulation keyword key
- =key= :: A one-argument function that transforms _both_ the tested value and
  the elements of the sequence passed to the function given by the =test=
  keyword.

  The keyword =key= is useful to avoid applying a transformation to the
  tested value more than once when searching through a long sequence, as would
  be done if it were called explicitly in =test=.

  #+begin_src emacs-lisp
    ;; => ((a . 1) (b . 2) (c . 4))
    (loopy (with (test #'car))
           (list i '((a . 1) (b . 2) (a . 3) (c . 4)))
           (adjoin i :at end :key #'car))

    ;; Similary to the above:
    ;;
    ;; => ((a . 1) (b . 2) (c . 4))
    (loopy (with (test-val))
           (list i '((a . 1) (b . 2) (a . 3) (c . 4)))
           (set test-val (car i))
           (adjoin i :test (lambda (seq-val _)
                             (equal (car seq-val)
                                    test-val))))
  #+end_src


The arguments to the =test= and =key= parameters can be literal quoted functions
or expressions which evaluate to functions, just like when using ~cl-union~,
~cl-adjoin~, and so on.  ~loopy~ knows how to expand efficiently for either
case.

*** Generic Accumulation
:PROPERTIES:
:CUSTOM_ID: accum-generic
:DESCRIPTION: Accumulating function output.
:END:

Generic accumulation commands are more explicit uses of the accumulation
variable.  They are very similar to updating a variable's value
using the =set= command and exist for situations not covered by the other
accumulation commands.

- =reduce= is like ~cl-reduce~, calling a function that receives (1) the
  accumulation variable and (2) the value to accumulate, in that order.
- =accumulate= works by calling a function that receives (1) the value to
  accumulate and (2) the accumulation variable, in that order.
- =set-accum= is the most generic, and works like =set= for only one value.


The commands are described in more detail below.

#+tindex: reduce
#+tindex: reducing
- =(reduce VAR EXPR FUNC)= :: Reduce =EXPR= into =VAR= by =FUNC=, like in
  ~cl-reduce~ and ~(funcall FUNC VAR EXPR)~.  =FUNC= is called with =VAR= as the
  first argument and =EXPR= as the second argument.  This is unlike
  =accumulate=, which gives =VAR= and =EXPR= to =FUNC= in the opposite order
  (that is, =EXPR= first, then =VAR=).

  This command also has the alias =reducing=.

  Note that the first accumulated value depends on the initial value of =VAR=.
  By default, the first accumulated value is the value of =EXPR=, not a result
  of calling =FUNC=.  However, if =VAR= has an initial value given by the =with=
  special macro argument, then the first accumulated value is the result of
  ~(funcall FUNC VAR EXPR)~, as also done in the subsequent steps of the loop.
  This use of =with= is similar to the =:initial-value= keyword argument used by
  ~cl-reduce~.

  #+begin_src emacs-lisp
    ;; => 6
    (loopy (list i '(1 2 3))
           (reduce i #'*))

    ;; Similar to the above:
    (loopy (list i '(1 2 3))
           (set loopy-result i (* i loopy-result))
           (finally-return loopy-result))

    ;; = > 6
    (loopy (with (my-reduction 0))
           (list i '(1 2 3))
           (reduce my-reduction i #'+)
           (finally-return my-reduction))

    ;; Similar to the above:
    (cl-reduce #'+ (list 1 2 3) :initial-value 0)
    (seq-reduce #'+ [1 2 3] 0)
  #+end_src

  This command also has the alias =callf=.  It is similar to using the
  function ~cl-callf~, except that the function argument is given last and
  must be quoted.  This alias is intended to help users remember argument
  order.

  #+begin_src emacs-lisp
    (loopy (with (my-reduction 0))
           (list i '(1 2 3))
           (callf my-reduction i #'+)
           (finally-return my-reduction))

    ;; Is similar to the above:
    (loopy (with (my-reduction 0))
           (list i '(1 2 3))
           (do (cl-callf + my-reduction i))
           (finally-return my-reduction))
  #+end_src

#+tindex: accumulate
#+tindex: accumulating
- =(accumulate|accumulating VAR EXPR FUNC)= :: Accumulate the result of applying
  function =FUNC= to =EXPR= and =VAR= like in ~(funcall FUNC EXPR VAR)~.  =EXPR=
  and =VAR= are used as the first and second arguments to =FUNC=, respectively.

  #+begin_src emacs-lisp
    ;; Call `(cons i my-accum)'
    ;;
    ;; => (2 1)
    (loopy (list i '(1 2))
           (accumulate my-accum i #'cons)
           (finally-return my-accum))

    ;; Works mostly the same as the above:
    (loopy (list i '(1 2))
           (set my-accum (cons i my-accum))
           (finally-return my-accum))

    ;; => ((3 1) (4 2 8 9 10))
    (loopy (with (accum1 nil)
                 (accum2 (list 8 9 10)))
           (list i '((1 2) (3 4)))
           (accumulate (accum1 accum2) i #'cons)
           (finally-return accum1 accum2))
  #+end_src

  This command also has the alias =callf2=.  It is similar to using the function
  ~cl-callf2~, except that the function argument is given last and must be
  quoted.  This alias is intended to help users remember argument order.

  #+begin_src emacs-lisp
    (loopy (list i '(1 2))
           (callf2 my-accum i #'cons)
           (finally-return my-accum))

    ;; Is the same as the above:
    (loopy (with (my-accum))
           (list i '(1 2))
           (do (cl-callf2 cons i my-accum))
           (finally-return my-accum))
  #+end_src

#+tindex: set-accum
#+tindex: setting-accum
- =(set-accum VAR EXPR)= :: Set the accumulation variable =VAR= to the
  value of =EXPR=.

  This command also has the alias =setting-accum=.

  This command is a basic wrapper around =set= for only one value.  Because this
  command cannot be optimized (as it does not construct a sequence), it is safe
  to access the implicit variable ~loopy-result~ in =EXPR=, so long as the
  variable is not being modified by another command for which that would be
  unsafe.

  #+begin_src emacs-lisp
    ;; => 6
    (loopy (with (loopy-result 0))
           (array i [1 2 3])
           (set-accum (+ loopy-result i)))

    ;; These are equivalent to the above example:

    ;; => 6
    (loopy (with (loopy-result 0))
           (array i [1 2 3])
           (set loopy-result (+ loopy-result i))
           (finally-return loopy-result))

    ;; => 6
    (loopy (with (loopy-result 0))
           (array i [1 2 3])
           (set-accum loopy-result (+ loopy-result i))
           (finally-return loopy-result))
  #+end_src

*** Numeric Accumulation
:PROPERTIES:
:CUSTOM_ID: accum-numeric
:DESCRIPTION: Using and returning numbers.
:END:

Numeric accumulation work on numbers, such as by repeatedly adding or
multiplying values together.

#+tindex: count
#+tindex: counting
- =(count VAR EXPR)= :: Count the number of times that =EXPR= evaluates to a
  non-nil value.  =VAR= starts at 0 and is incremented by 1 each time.

  This command also has the alias =counting=.

  #+BEGIN_SRC emacs-lisp
    ;; => 3
    (loopy (list i '(1 nil 3 nil 5))
           (count non-nil-count i)
           (finally-return non-nil-count))
  #+END_SRC

#+tindex: max
#+tindex: maxing
#+tindex: maximize
#+tindex: maximizing
- =(maximize|max VAR EXPR)= :: Repeatedly set =VAR= to the greater of the values
  =VAR= and =EXPR=.  =VAR= starts at negative infinity (~-1.0e+INF~), so that
  any other value should be greater that it.

  This command also has the aliases =maximizing= and =maxing=.

  #+BEGIN_SRC emacs-lisp
    ;; => 11
    (loopy (list i '(1 11 2 10 3 9 4 8 5 7 6))
           (maximize my-max i)
           (finally-return my-max))
  #+END_SRC

#+tindex: min
#+tindex: minimize
#+tindex: minnning
#+tindex: minimizing
- =(minimize|min VAR EXPR)= :: Repeatedly set =VAR= to the lesser of the values
  =VAR= and =EXPR=.  =VAR= starts at positive infinity (~1.0e+INF~), so that any
  other value should be less than it.

  This command also has the aliases =minimizing= and =minning=.

  #+BEGIN_SRC emacs-lisp
    ;; => 0
    (loopy (list i '(1 11 2 10 3 0 9 4 8 5 7 6))
           (minimize my-min i)
           (finally-return my-min))
  #+END_SRC

#+tindex: multiply
#+tindex: multiplying
- =(multiply VAR EXPR)= :: Repeatedly set =VAR= to the product of
  the values =EXPR= and =VAR=.  =VAR= starts at 1.

  This command also has the alias =multiplying=.

  #+BEGIN_SRC emacs-lisp
    ;; => 120
    (loopy (list i '(1 2 3 4 5))
           (multiply 5-factorial i)
           (finally-return 5-factorial))
  #+END_SRC

#+tindex: sum
#+tindex: summing
- =(sum VAR EXPR)= :: Repeatedly set =VAR= to the sum of the values of =EXPR=
  and =VAR=.  =VAR= starts at 0.

  This command also has the alias =summing=.

  #+BEGIN_SRC emacs-lisp
    ;; => 10
    (loopy (list i '(1 2 3 4))
           (sum my-sum i)
           (finally-return my-sum))
  #+END_SRC

*** Sequence Accumulation
:PROPERTIES:
:DESCRIPTION: Using and returning sequences.
:CUSTOM_ID: accum-sequence
:END:

Sequence accumulation commands are used to join lists (such as =union= and
=append=) and to collect items into lists (such as =collect= and =adjoin=).

#+tindex: adjoin
#+tindex: adjoining
- =(adjoin VAR EXPR &key at test key)= :: Repeatedly add =EXPR= to =VAR= if it
  is not already present in the list.

  This command also has the alias =adjoining=.

  Unlike ~cl-adjoin~ and like the other accumulation commands, this command
  defaults to adjoining =EXPR= to the end of =VAR=, not the beginning.

  Unlike Common Lisp's Iterate, Loopy's =adjoin= command intentionally does
  not provide a =:result-type= keyword.  Instead, manipulate the accumulation
  variable (by default, ~loopy-result~) directly, such as with ~seq-into~.

  #+begin_src emacs-lisp
    ;; => ((1 . 1) (1 . 2) (2 . 3))
    (loopy (list i '((1 . 1) (1 . 2) (1 . 2) (2 . 3)))
           (adjoin i))

    ;; No `:result-type', use `seq-into' on `loopy-result':
    ;;
    ;; => [1 2 3 4]
    (loopy (list i '(1 2 3 3 4))
           (adjoin i)
           (finally-return (seq-into loopy-result 'vector)))

    ;; => (4 3 2 1)
    (loopy (list i '(1 2 3 3 4))
           (adjoin my-var i :at 'start)
           (finally-return my-var))
  #+end_src

#+tindex: append
#+tindex: appending
- =(append VAR EXPR &key at)= :: Repeatedly concatenate =EXPR= to =VAR=, as if
  by the function ~append~.

  This command also has the alias =appending=.

  #+BEGIN_SRC emacs-lisp
    ;; => '(1 2 3 4 5 6)
    (loopy (list i '((1 2 3) (4 5 6)))
           (append coll i)
           (finally-return coll))

    ;; => (4 5 6 1 2 3)
    (loopy (list i '((1 2 3) (4 5 6)))
           (append i :at start))
  #+END_SRC

#+tindex: collect
#+tindex: collecting
- =(collect VAR EXPR &key at)= :: Collect the value of =EXPR= into
  the list =VAR=.  By default, elements are added to the end of the list.

  This command also has the alias =collecting=.

  Unlike Common Lisp's Iterate, Loopy's =collect= command intentionally does
  not provide a =:result-type= keyword.  Instead, manipulate the accumulation
  variable (by default, ~loopy-result~) directly, such as with ~seq-into~ or
  ~cl-coerce~.

  #+BEGIN_SRC emacs-lisp
    ;; => '(1 2 3)
    (loopy (list i '(1 2 3))
           (collect i))

    ;; => '((1 2 3) ((1) (1 2) (1 2 3)))
    (loopy (list i '(1 2 3))
           ;; Collect `i' into `coll1'.
           (collect coll1 i)
           ;; Collect `coll1' into a generated variable.
           (collect coll1)
           (finally-return coll1 loopy-result))

    ;; => [1 2 3]
    (loopy (list j '(1 2 3))
           (collect j)
           (finally-return (cl-coerce loopy-result 'vector)))

    ;; => (3 2 1)
    (loopy (list j '(1 2 3))
           (collect j :at start))

    ;; => (1 2 3)
    (loopy (list j '(1 2 3))
           (collect j :at 'end))
  #+END_SRC

#+tindex: concat
#+tindex: concating
- =(concat VAR EXPR &key at)= :: Repeatedly ~concat~ the value of =EXPR= onto
  =VAR=, as a string.  For concatenating values into a vector, see the command
  =vconcat=.

  This command also has the alias =concating=.

  #+BEGIN_SRC emacs-lisp
    ;; => "abc"
    (loopy (list i '("a" "b" "c"))
           (concat str i)
           (finally-return str))

    ;; => ("da" "eb" "fc")
    (loopy (list j '(("a" "b" "c") ("d" "e" "f")))
           (concat (str1 str2 str3) j :at 'start)
           (finally-return str1 str2 str3))
  #+END_SRC

#+tindex: nconc
#+tindex: nconcing
- =(nconc VAR EXPR &key at)= :: Repeatedly and /destructively/ concatenate the
  value of =EXPR= onto =VAR= as if by using the function ~nconc~.

  This command also has the alias =nconcing=.

  #+attr_texinfo: :tag Caution
  #+begin_quote
  ~nconc~ is a destructive operation that modifies =VAR= directly
  ([[info:elisp#Rearrangement]]).  This is important to keep in mind when working
  with literal values, such as the list ='(1 2 3)=, whose modification could
  apply wherever that value is used ([[info:elisp#Self-Evaluating Forms]]).
  #+end_quote

  #+BEGIN_SRC emacs-lisp
    ;; => '(1 2 3 4 5 6 7 8)
    (loopy (list i '((1 2 3 4) (5 6 7 8)))
           (nconc my-new-list i)
           (finally-return my-new-list))

    ;; => '(3 3 3 2 2 1)
    (loopy (list i (list (make-list 1 1)
                         (make-list 2 2)
                         (make-list 3 3)))
           (nconc i :at start))
  #+END_SRC

#+tindex: nunion
#+tindex: nunioning
- =(nunion VAR EXPR &key test at key)= :: Repeatedly and /destructively/ insert
  into =VAR= the elements of =EXPR= which are not already present in =VAR=.

  This command also has the alias =nunioning=.

  #+begin_src emacs-lisp
    ;; => (4 1 2 3)
    (loopy (list i '((1 2) (2 3) (3 4)))
           (nunion var i)
           (finally-return var))

    ;; => (4 2 (1 1) 3)
    (loopy (list i '(((1 1) 2) ((1 1) 3) (3 4)))
           (nunioning var i :test #'equal)
           (finally-return var))

    ;; => ((1 2 3) (2 3 4))
    (loopy (array i [((1 2) (2 3))
                     ((1 2 3) (3 4))])
           (nunion (var1 var2) i :test #'equal)
           (finally-return var1 var2))
  #+end_src

#+tindex: prepend
#+tindex: prepending
- =(prepend VAR EXPR)= :: Repeatedly concatenate =EXPR= onto the front of =VAR=,
  as if by the function ~append~.

  This command also has the alias =prepending=.

  This command is interpreted by Loopy as =(append VAR EXPR :at start)=, and is
  normally described as such when reporting errors.  It exists for clarity and
  convenience.

  #+begin_src emacs-lisp
    ;; => (5 6 3 4 1 2)
    (loopy (array i [(1 2) (3 4) (5 6)])
           (prepend i))

    ;; => (4 3 2 1)
    (let ((my-list '(1)))
      (loopy (without my-list)
             (array elem [(2) (3) (4)])
             (prepend my-list elem)
             (finally-return my-list)))
  #+end_src

#+tindex: push
#+tindex: pushing
#+tindex: push-into
#+tindex: pushing-into
- =(push-into|push VAR EXPR)= :: Collect the value of =EXPR= into a list, adding
  values to the front of =VAR= as if by using the function ~push~.

  This command also has the alias =pushing= and =pushing-into=.

  This command is interpreted by Loopy as =(collect VAR EXPR :at start)=, and is
  normally described as such when reporting errors.  It exists for clarity and
  convenience.

  #+BEGIN_SRC emacs-lisp
    ;; => (3 2 1)
    (loopy (array i [1 2 3])
           (push my-list i)
           (finally-return my-list))
  #+END_SRC

#+tindex: union
#+tindex: unioning
- =(union VAR EXPR &key test at key)= :: Repeatedly insert into =VAR= the
  elements of the list =EXPR= that are not already present in =VAR=.

  This command also has the alias =unioning=.

  #+begin_src emacs-lisp
    ;; => (4 1 2 3)
    (loopy (list i '((1 2) (2 3) (3 4)))
           (union var i)
           (finally-return var))

    ;; => (4 2 (1 1) 3)
    (loopy (list i '(((1 1) 2) ((1 1) 3) (3 4)))
           (unioning var i :test #'equal)
           (finally-return var))

    ;; => ((1 2 3) (2 3 4))
    (loopy (array i [((1 2) (2 3))
                     ((1 2 3) (3 4))])
           (union (var1 var2) i :test #'=)
           (finally-return var1 var2))
  #+end_src

#+tindex: vconcat
#+tindex: vconcating
- =(vconcat VAR EXPR &key at)= :: Repeatedly concatenate the value of =EXPR=
  onto =VAR= via the function ~vconcat~.  For concatenating values into a
  string, see the command =concat=.

  This command also has the alias =vconcating=.

  #+BEGIN_SRC emacs-lisp
    ;; => [1 2 3 4 5 6]
    (loopy (list i '([1 2 3] [4 5 6]))
           (vconcat my-vector i)
           (finally-return my-vector))

    ;; => [4 5 6 1 2 3]
    (loopy (list i '([1 2 3] [4 5 6]))
           (vconcat i :at 'start))
  #+END_SRC

*** Other Accumulation Commands

#+tindex: find
#+tindex: finding
- =(find VAR EXPR TEST &key ON-FAILURE)= :: If the expression =TEST= is non-nil,
  then the loop stops and =VAR= is set to the value of =EXPR=.  If =TEST= is
  never non-nil, then =VAR= is set to the value of =ON-FAILURE=, if provided.

  This command also has the alias =finding=.

  #+BEGIN_SRC emacs-lisp
    ;; => (13 (1 2))
    (loopy (list i '(1 2 3 4 5 6 7 8))
           (find (+ i 10) (> i 2))
           (collect coll i)
           (finally-return loopy-result coll))

    ;; => nil
    (loopy (list i '(1 2 3 4 5 6))
           (find i (> i 12)))

    ;; => 27
    (loopy (list i '(1 2 3 4 5 6))
           (find i (> i 12) :on-failure 27))

    ;; => nil
    (loopy (list i '(1 2 3 4 5 6))
           (find nil (> i 3) :on-failure 27))
  #+END_SRC

  If the loop is left early and =TEST= was never non-nil, then this is the same
  as a normal failure and =VAR= will be set to the value of =ON-FAILURE=, if
  provided.

  #+begin_src emacs-lisp
  ;; => 27
  (loopy (list i '(1 2 3 4 5 6))
         (while (< i 3))
         (find i (> i 12) :on-failure 27))
  #+end_src

  Like in Common Lisp's Iterate package, if =TEST= is a sharp-quoted symbol,
  such as ~#'cl-evenp~, then the test expression is treated as applying that
  function to the value of =EXPR=.

  #+begin_src emacs-lisp
    ;; => 2
    (loopy (list i '(1 2 3 4 5 6))
           ;; (find i (cl-evenp i))
           (find i #'cl-evenp))
  #+end_src

  To be consistent with other commands, =ON-FAILURE= is evaluated at the
  start of the loop, even though that is not necessarily where it is used.

  #+BEGIN_SRC emacs-lisp
    ;; Value of `:on-failure' gotten at the start of the loop:
    ;; => 27
    (loopy (with (on-fail 27))
           (list i '(1 2 3))
           (set on-fail 33)
           (find i (> i 4) :on-failure on-fail))
  #+END_SRC

***  Optimizing Accumulations
:PROPERTIES:
:CUSTOM_ID: optimized-accums
:DESCRIPTION: Producing efficient accumulations.
:END:

#+cindex: optimizing accumulations
By default, named accumulation variables (excluding the automatically named
~loopy-result~) are accessible during the loop, and their values are always
correct during execution.  For example, lists are always in the correct order,
even when constructing linked lists in the reverse order would be more
efficient.

#+begin_src elisp
  ;; Note how `coll' is always in the correct order:
  ;; => ((1 2 3)
  ;;     ((1) (1 2) (1 2 3)))
  (loopy (array i [1 2 3])
         (collect coll i)
         (collect intermediate-values (copy-sequence coll))
         (finally-return coll intermediate-values))
#+end_src

Implied accumulation variables are not required to always be in the correct
order, so commands using such variables can produce more efficient code.

#+begin_src emacs-lisp
  ;; Similar in efficiency to the below `dotimes':
  ;;
  ;; => (2 3 4 5 6 7 8 9 10 11 12 13 ...)
  (loopy (numbers i :to 1000)
         (collect (1+ i)))

  ;; => (2 3 4 5 6 7 8 9 10 11 12 13 ...)
  (let (result)
    (dotimes (i 1000)
      (push (1+ i) result))
    (nreverse result))
#+end_src

The situation becomes more complex when commands place values at both sides of a
sequence.  In that case, Loopy keeps track of the beginning /and/ the end of
the sequence.  Loopy /does not/ merely append to the end of the accumulating
list, since that would be much slower for large lists.

#+begin_src emacs-lisp
  ;; `loopy' can be faster than the below `dolist' approach:
  ;; => (3 2 1 7 14 21)
  (loopy (list i '(1 2 3))
         (collect i :at start)
         (collect (* i 7) :at end))

  ;; For large accumulations, this is slower than the above:
  ;; => (3 2 1 7 14 21)
  (let (result)
    (dolist (i '(1 2 3))
      (setq result (cons i result)
            result (nconc result (list (* i 7)))))
    result)
#+end_src

In such cases, ~loopy~ will naively optimize placing values at whichever side of
the sequences appears to be more used.  In the example below, note that even
though the commands to insert values at the front of the list are never actually
run, ~loopy~ will still optimize for frontal insertions.  Here, ~loopy~ simply
counts that 2 commands seem to place values at the front of the list and that
only 1 command seems to place values at the end.

#+begin_src emacs-lisp
  ;; This code optimizes for insertions at the front of the list:
  ;; => (1 2 3)
  (loopy (list i '(1 2 3))
         (collect i :at end)
         (when nil
           (collect i :at start)
           (collect i :at start)))
#+end_src

The special macro argument =accum-opt= can be used to better control these
optimizations ([[#macro-arguments]]).  With it, you can (1) treat an explicit
variable as if it were implicit and optionally (2) specify which side of a
sequence you expect to use more.  The arguments passed to =accum-opt= are either
symbols (such as ~loopy-result~) or lists of a symbol and a position.  To be
clear, ~loopy-result~, when used as an implied variable, is always at least
naively optimized in the manner described above.

In the example below, see that
1. Accumulation into the named variable ~coll~ has been explicitly optimized for
   using the end of the sequence, despite there being more commands that use the
   beginning of the sequence.
2. Because accumulation into ~coll~ has been optimized, the order of values in
   ~coll~ need not be correct during the loop.


#+begin_src emacs-lisp
  ;; This code optimizes for insertions at the end of `coll':
  ;;
  ;; => ((23 13 22 12 21 11 1 2 3)
  ;;     ((1 11 21) (2 1 11 21 12 22) (3 2 1 11 21 12 22 13 23)))
  (loopy (accum-opt (coll end))
         (list i '(1 2 3))
         (collect coll i :at end)
         (collect coll (+ 10 i) :at start)
         (collect coll (+ 20 i) :at start)
         (collect intermediate-values (copy-sequence coll))
         (finally-return coll intermediate-values))
#+end_src

The =accump-opt= special macro argument can also be used with destructuring.
Because destructuring requires using named variables, such variables are by
default required to be ordered correctly during the loop.  If you do not need
them to be so, you are recommended to use =accum-opt= on those variables.

#+begin_src emacs-lisp
  ;; => ((1 3)
  ;;     (2 4)
  ;;     ((1) (3 1))
  ;;     ((2) (4 2)))
  (loopy (accum-opt a b)
         (array elem [(1 2) (3 4)])
         (collect (a b) elem)
         (collect intermediate-a (copy-sequence a))
         (collect intermediate-b (copy-sequence b))
         (finally-return a b intermediate-a intermediate-b))
#+end_src

** Checking Conditions
:PROPERTIES:
:CUSTOM_ID: boolean-commands
:DESCRIPTION: Testing whether a condition holds true.
:END:

{{{dfn(Boolean commands)}}} are used to test whether a condition holds true
during the loop.  They work like a combination of accumulation commands
([[#accumulation-commands]]) and early-exit commands ([[#exiting-the-loop-early]]), in
that values are by default stored in ~loopy-result~ and that they can terminate
the loop without forcing a return value.

#+ATTR_TEXINFO: :tag Note
#+BEGIN_QUOTE
Due to how the commands work, there are restrictions to how their target
variables can be used.  First, the =always= and =never= commands must use the
same variable to work together correctly.  Second, using the command =thereis=
with the same variable as =always= (and/or =never=) is an error, as this would
create conflicting initial values for the implicit return value.
#+END_QUOTE


#+tindex: always
- =(always [VAR] EXPR &key into)= :: Check the result of the condition =EXPR=.
  If the condition evaluates to ~nil~, end the loop.  If the command was run,
  return the value of the condition via =VAR=.  Otherwise, if the command was
  never run, return ~t~ via =VAR=.

  The steps are thus:
  1. The variable (by default, ~loopy-result~) is initially bound to ~t~, using
     it as the implicit return value of the loop.
  2. When the condition is checked, the variable is bound to the value of
     the condition.
  3. If the variable is ~nil~, the loop is exited.
  4. If the loop completes successfully, then the variable is the final value
     of the condition.  If the command is never run, then the variable will
     remain ~t~.


  #+BEGIN_SRC emacs-lisp
    ;; => t
    (loopy (list i '(1 0 1 0 1))
           (always (< i 2)))

    ;; Returns the final value of the condition:
    ;; => 5
    (loopy (list i '(1 0 1 0 1))
           (always (and (< i 2) 5)))

    ;; => nil
    (loopy (list i '(1 0 1 0 1))
           (always (< i 1)))

    ;; NOTE: Here, the implicit return value is `t' because an
    ;;       `always' command was used, and that return value
    ;;       is never updated to "hello" because the `always'
    ;;       command is never actually run.
    ;;
    ;; => t
    (loopy (list i '(1 1 1 1))
           (when nil
             (always (and (> i 5) "hello"))))
  #+END_SRC

#+tindex: never
- =(never [VAR] EXPR &key into)= :: Check the condition =EXPR=.  If the
  condition is ever non-~nil~, then the loop is exited and returns ~nil~ via
  =VAR=.  Otherwise the loop returns ~t~ via =VAR=.

  The steps are thus:
  1. The variable (by default, ~loopy-result~) is initialized to ~t~ and used as
     the loop's implicit return value.
  2. The value of the condition is checked.
  3. If the condition is non-~nil~, then the variable is set to ~nil~
     and the loop is exited.


  #+attr_texinfo: :tag Note
  #+begin_quote
  Unlike the =always= command, =never= does not store any information in the
  variable until it ends the loop.  Therefore, so long as the conditions of
  =never= are always ~nil~, =never= does not affect the loop's implicit return
  value when using the =always= command.

  Be aware, though, that this behavior depends on =always= and =never= using
  the same variable.
  #+end_quote

  #+BEGIN_SRC emacs-lisp
    ;; => t
    (loopy (list i '(1 0 1 0 1))
           (never (= i 3)))

    ;; => nil
    (loopy (list i '(1 0 1 0 1))
           (never (= i 0)))

    ;; This example taken from the documentation of CL's Iterate package.
    ;;
    ;; => 2, not t
    (loopy (cycle 2)
           (always 2)
           (never nil))
  #+end_src

#+tindex: thereis
- =(thereis [VAR] EXPR &key into)= :: Check the result of the condition =EXPR=.
  If the condition evaluates to a non-~nil~ value, the loop returns that value
  via =VAR=.  Otherwise, the loop returns ~nil~ via =VAR=.

  The steps are thus:
  1. The variable (by default, ~loopy-result~) is initialized to ~nil~ and used
     as the implicit return value of the loop.
  2. The value of the condition is stored in the variable.
  3. If the value of the variable is non-~nil~, the loop exits.


  #+BEGIN_SRC emacs-lisp
    ;; => 3
    (loopy (list i '(1 0 1 3 1))
           ;; Note: `and' returns the last value it evaluates.
           (thereis (and (> i 2) i)))

    ;; => nil
    (loopy (list i '(1 0 1 0 1))
           (thereis (and (> i 2) i)))

    ;; => 7
    (loopy (list i '(nil nil 3 nil))
           (thereis i)
           (finally-return (+ loopy-result 4)))
  #+END_SRC


** Control Flow
:PROPERTIES:
:CUSTOM_ID: control-flow
:DESCRIPTION: When to run loop commands.
:END:

This section describes:
- how to conditionally execute loop commands
- how to skip loop cycles
- how to leave the loop early, with or without forcing a return value

*** Conditional Execution
:PROPERTIES:
:CUSTOM_ID: conditionals
:DESCRIPTION: Choosing if commands should run.
:END:

{{{dfn(Conditional execution commands)}}} in ~loopy~ can take multiple
sub-commands, and work like their Lisp counterparts.  There is therefore no need
for an =and= command as used in ~cl-loop~.

#+tindex: cond
- =(cond [(EXPR CMDS) [...]])= :: Run the commands =CMDS= following the first
  non-nil condition =EXPR=.  This is the ~loopy~ version of the ~cond~ special
  form from normal Emacs Lisp.

  #+BEGIN_SRC emacs-lisp
    ;; => '((2 4 6) (1 3 5) ("cat" "dog"))
    (loopy (list i '(1 2 3 "cat" 4 5 6 "dog"))
           (cond
            ((not (numberp i)) (collect not-numbers i))
            ((cl-evenp i)      (collect evens i))
            (t                 (collect odds i)))
           (finally-return evens odds not-numbers))
  #+END_SRC

#+tindex: if
- =(if EXPR CMDS)= :: Run the first command if =EXPR= is non-nil.  Otherwise,
  run the remaining commands.  This is the ~loopy~ version of the ~if~ special
  form from normal Emacs Lisp.

  #+BEGIN_SRC emacs-lisp
    ;; => '((1 3 5 7) (2 4 6) (3 3 3))
    (loopy (sequence i [1 2 3 4 5 6 7])
           (if (cl-oddp i)
               (collect odds i)
             (collect evens i)
             (collect some-threes 3))
           (finally-return odds evens some-threes))
  #+END_SRC

#+tindex: when
- =(when EXPR CMDS)= :: Run =CMDS= only if =EXPR= is non-nil.  This is the
  ~loopy~ version of the ~when~ macro from normal Emacs Lisp.

  #+BEGIN_SRC emacs-lisp
    ;; Get only the inner lists with all even numbers:
    ;;
    ;; => '((2 4 6) (8 10 12) (16 18 20))
    (loopy (list i '((2 4 6) (8 10 12) (13 14 15) (16 18 20)))
           (when (loopy (list j i)
                        (always (cl-evenp j)))
             (collect only-evens i))
           (finally-return only-evens))
  #+END_SRC

#+tindex: unless
- =(unless EXPR CMDS)= :: Run =CMDS= only if =EXPR= is nil.  This is the ~loopy~
  version of the ~unless~ macro from normal Emacs Lisp.

  #+BEGIN_SRC emacs-lisp
    ;; Get only the inner lists with all even numbers:
    ;;
    ;; => '((2 4 6) (8 10 12) (16 18 20))
    (loopy (list i '((2 4 6) (8 10 12) (13 14 15) (16 18 20)))
           (unless (loopy (list j i)
                          (thereis (cl-oddp j)))
             (collect only-evens i))
           (finally-return only-evens))
  #+END_SRC

*** Skipping Cycles
:PROPERTIES:
:CUSTOM_ID: skipping-an-iteration
:DESCRIPTION: Immediately beginning the next iteration.
:END:

{{{dfn(Skip commands)}}} are used to skip iteration cycles in the loop,
immediately jumping to the next iteration.  They are equivalent to the
~continue~ statement used by Python and other languages.  For this reason, they
have aliases that use the word "continue" instead of the word "skip".

#+tindex: skip
#+tindex: continue
#+tindex: skipping
#+tindex: continuing
- =(skip|continue)= :: Skip the remaining commands and continue to the next loop
  iteration.

  This command also has the aliases =skipping= and =continuing=.

  #+BEGIN_SRC emacs-lisp
    ;; => (2 4 6 8 10 12 14 16 18 20)
    (loopy (sequence i (number-sequence 1 20))
           (when (cl-oddp i) (skip))
           (collect i))
  #+END_SRC

#+tindex: skip-from
#+tindex: continue-from
#+tindex: skipping-from
#+tindex: continuing-from
- =(skip-from|continue-from NAME)= :: Skip the remaining commands and continue
  to the next loop iteration of the loop =NAME=.

  This command also has the aliases =skipping-from= and =continuing-from=.

  #+BEGIN_SRC emacs-lisp
    ;; => ((1 2 3) (7 8 9))
    (loopy outer
           (array i [(1 2 3) (4 5 6) (7 8 9)])
           (loopy (list j i)
                  (when (= 5 j)
                    (skip-from outer)))
           (collect i))
  #+END_SRC


*** Early Exit
:PROPERTIES:
:CUSTOM_ID: exiting-the-loop-early
:DESCRIPTION: Leaving the loop early, with or without returning values.
:END:

{{{dfn(Early-exit commands)}}} are used to leave the loop before it completes.
The loop is contained in a ~cl-block~, which can be exited by the function
~cl-return-from~.  Indeed, the =return= and =return-from= commands described
below are just wrappers around that function.  As with the =finally-return=
special macro argument, passing multiple return values to =return= and
=return-from= will return a list of those values.  If no value is given, ~nil~
is returned.

In Loopy, implied accumulation variables can be modified a final time after the
loop exits in order to finalize their values.  For example, if an optimized
accumulated list is built in reverse, then it will be reversed into the correct
order after the loop completes.  Loopy has "return" commands for immediately
returning a value from the loop without finalizing values and "leave" commands
for leaving the loop without forcing a return value, allowing values to be
finalized.

#+begin_src emacs-lisp
  ;; An example of not finalizing the accumulated value:
  ;;
  ;; => (4 3 2 1 0)
  (loopy (numbers i :to 10)
         (if (< i 5)
             (collect i)
           ;; Not finalized:
           (return loopy-result)))

  ;; An example of finalizing the accumulated value:
  ;;
  ;; => (0 1 2 3 4)
  (loopy (numbers i :to 10)
         (if (< i 5)
             (collect i)
           (leave))
         (finally-return loopy-result))
#+end_src

As noted in [[#macro-arguments]], the return value of Lisp code given in the special
macro argument =finally-do= does not affect the return value of the loop.
However, =finally-do= can modify the values of variables after the loop
completes, including ~loopy-result~ (even when used as an implied accumulation
variable).

#+begin_src emacs-lisp
  ;; => (0 1 2 3 4)
  (loopy (numbers i :to 10)
         (if (< i 5)
             (collect i)
           (leave))
         (finally-do 7))

  ;; _Does not_ modify the then-current value of `loopy-result'
  ;; returned by the `return' command:
  ;;
  ;; => (4 3 2 1 0)
  (loopy (numbers i :to 10)
         (if (< i 5)
             (collect i)
           ;; Not finalized:
           (return loopy-result))
         (finally-do
          ;; Affects what is stored in `loopy-result',
          ;; but because of the `return' command,
          ;; not what the macro actually returns.
          (cl-callf2 cons 22 loopy-result)))

  ;; _Does_ modify the value of `loopy-result' returned by the macro:
  ;;
  ;; => (0 1 2 3)
  (loopy (list i '(1 2 3))
         (collect loopy-result i)
         (finally-do (push 0 loopy-result))
         (finally-return loopy-result))

  ;; _Does_ modify the value of `loopy-result' returned by the macro:
  ;;
  ;; => (0 1 2 3)
  (loopy (list i '(1 2 3))
         (collect i)
         (finally-do (push 0 loopy-result)))
#+end_src

As noted in [[#macro-arguments]], the special macro argument =finally-return=
overrides the return value of the loop, including values that would have been
returned by any "return" commands.

#+begin_src emacs-lisp
  ;; => 22
  (loopy (numbers i :to 10)
         (if (< i 5)
             (collect i)
           ;; Not finalized:
           (return loopy-result))
         (finally-return 22))

  ;; => 22
  (loopy (numbers i :to 10)
         (if (< i 5)
             (collect i)
           (leave))
         (finally-return 22))
#+end_src


#+tindex: leave
#+tindex: leaving
- =(leave)= :: Leave the current loop without forcing a return value.

  This command also has the alias =leaving=.

  #+begin_src emacs-lisp
    ;; => (1 2 3 4)
    (loopy (list i '(1 2 3 4 5 6 7))
           (if (= i 5)
               (leave)
             (collect i)))
  #+end_src

#+tindex: leave-from
#+tindex: leaving-from
- =(leave-from NAME)= :: Leave the loop =NAME= without forcing a return value.
  This command is equivalent to =(at NAME (leave))= ([[#sub-loops]]).

  This command also has the alias =leaving-from=.

  #+begin_src emacs-lisp
    ;; => ([2 4] [6 8])
    (loopy outer
           (list i '([2 4] [6 8] [7 10]))
           (loopy (array j i)
                  (when (cl-oddp j)
                    ;; Equivalent to `(at outer (leave))'
                    (leave-from outer)))
           (collect i))
  #+end_src

#+tindex: return
#+tindex: returning
- =(return [EXPRS])= :: Return from the current loop without finalizing values,
  returning =[EXPRS]=.

  This command also has the alias =returning=.

  #+BEGIN_SRC emacs-lisp
    ;; => 6
    (loopy (with  (j 0))
           (do (cl-incf j))
           (when (> j 5)
             (return j)))
  #+END_SRC

#+tindex: return-from
#+tindex: returning-from
- =(return-from NAME [EXPRS])= :: Return from the loop =NAME=, returning
  =[EXPRS]=.  This command is equivalent to =(at NAME (return))= ([[#sub-loops]]).

  This command also has the alias =returning-from=.

  #+BEGIN_SRC emacs-lisp
    ;; => 'bad-val?
    (loopy (named outer-loop)
           (list inner-list '((1 2 3) (1 bad-val? 1) (4 5 6)))
           (loopy (list i inner-list)
                  (when (eq i 'bad-val?)
                    (return-from outer-loop 'bad-val?))))
  #+END_SRC

#+tindex: while
- =(while COND)= :: Leave the loop once =COND= is false, without forcing a
  return value.  =(while COND)= is the same as =(until (not COND))=
  and =(unless COND (leave))=.

  #+begin_src emacs-lisp
    ;; => (1 2 3 4)
    (loopy (list i '(1 2 3 4 5 6 7))
           (while (/= i 5))
           (collect i))

    ;; Same as the above:
    ;;
    ;; => (1 2 3 4)
    (loopy (list i '(1 2 3 4 5 6 7))
           (unless (/= i 5) (leave))
           (collect i))
  #+end_src

#+tindex: until
- =(until COND)= :: Leave the loop once =COND= is true, without forcing a return
  value.  =(until COND)= is the same as =(while (not COND))=
  and =(when COND (leave))=.

  #+begin_src emacs-lisp
    ;; => (1 2 3 4)
    (loopy (list i '(1 2 3 4 5 6 7))
           (until (= i 5))
           (collect i))

    ;; Same as the above:
    ;;
    ;; => (1 2 3 4)
    (loopy (list i '(1 2 3 4 5 6 7))
           (when (= i 5) (leave))
           (collect i))
  #+end_src

** Sub-Loops
:PROPERTIES:
:DESCRIPTION: Running a loop within a loop.
:CUSTOM_ID: sub-loops
:END:

Loopy provides two sets of commands for working with sub-loops:
1. The =loopy= and =loopy-iter= commands, which correctly expand inner loops
   during the expansion of outer loops.
2. The =at= command, which controls the named outer loop that commands interact
   with.  For example, it can control to which loop an implied accumulation
   variable is scoped, which determines which loop uses that variable as an
   implied return value.

In the example below, the arguments of the =do= command are inserted into the
loop body literally, so by the time the inner loop expands, the outer loop might
have already been expanded into normal Emacs Lisp code, in which case the inner
macro would not find any outer Loopy loop named "outer".

#+begin_src emacs-lisp
  ;; Can signal an error or not work as expected:
  (loopy (named outer)
         (list i '((1 2) (3 4) (5 6)))
         (do (loopy (list j i)
                    (when (= j 5)
                      (leave-from outer))))
         (collect i))
#+end_src

In general, correct code requires that inner loops be expanded during the
expansion of the outer loop, as done with other commands.

#+begin_src emacs-lisp
  ;; Works as expected:
  ;;
  ;; => ((1 2) (3 4))
  (loopy outer
         (list i '((1 2) (3 4) (5 6)))
         (loopy (list j i)
                (when (= j 5)
                  (leave-from outer)))
         (collect i))
#+end_src

#+attr_texinfo: :tag Warning
#+begin_quote
Don't confuse using these sub-loop commands with using calls to the macros
~loopy~ and ~loopy-iter~.  For example, the =EXPR= parameter to loop commands is
used literally, and is not guaranteed to be able to affect macro expansion.
#+end_quote

The commands are described in more detail below.

#+tindex: loopy
- =(loopy [SPECIAL-MACRO-ARGUMENTS or CMDS])= :: Use the ~loopy~ macro as a
  loop command.

  #+begin_src emacs-lisp
    ;; => (1 11 2 12 3 13 4 14)
    (loopy outer
           (list i '([1 2] [3 4] 'bad [5 6] [7 8]))
           (loopy (unless (arrayp i)
                    (leave-from outer))
                  (array j i)
                  (at outer
                      (collect j)
                      (collect (+ j 10)))))
  #+end_src

#+tindex: loopy-iter
- =(loopy-iter [SPECIAL-MACRO-ARGUMENTS or CMDS or LISP-EXPRS])= :: Use the
  ~loopy-iter~ macro as a loop command ([[#loopy-iter]]).

  This feature can only be used after first loading the library =loopy-iter=.

  #+begin_src elisp
    (require 'loopy-iter)

    ;; => (1 11 2 12 3 13 4 14)
    (loopy outer
           (list i '([1 2] [3 4] 'bad [5 6] [7 8]))
           (loopy-iter (unless (arrayp i)
                         (leaving-from outer))
                       (arraying j i)
                       (cl-flet ((10+ (x) (+ x 10)))
                         (at outer
                             (collecting j)
                             (collecting (10+ j))))))
  #+end_src

#+tindex: at
- =(at LOOP-NAME [CMDS])= :: Parse commands with respect to the super-loop
  =LOOP-NAME=.  For example, a =leave= subcommand would exit the super-loop
  =LOOP-NAME=, and an accumulation command would create a variable in that
  super-loop.

  If one did not use =at= in the below example, then the accumulation would be
  local to the sub-loop and the return value of the loop =outer= would be ~nil~.

  #+begin_src emacs-lisp
    ;; => (4 5 10 11 16 17)
    (loopy outer
           (array i [(1 2) (3 4) (5 6)])
           (loopy (with (sum (apply #'+ i)))
                  (list j i)
                  (at outer (collect (+ sum j)))))
  #+end_src

  Keep in mind that the effects of flags ([[#flags]]) are local to the loops in
  which they are used, even when using the =at= command.

  #+begin_src emacs-lisp
    ;; => ((1 2 11 12)
    ;;     ((2) (3) (12) (13)))
    (loopy outer
           (flag pcase)
           (array elem [(1 2) (11 12)])
           (collect `(,first . ,rest) elem)
           ;; NOTE: The sub-loop uses the default destructuring style.
           ;;       The `pcase' style only affects the surrounding loop.
           (loopy (at outer (collect (first &rest rest) (mapcar #'1+ elem)))
                  (leave))
           (finally-return first rest))
  #+end_src

* Destructuring Macros
:PROPERTIES:
:CUSTOM_ID: destr-macros
:DESCRIPTION: Destructuring outside of the loop.
:END:

#+cindex: destructuring macros
The below macros make Loopy's built-in destructuring system available for
general use ([[#basic-destructuring]]).  For example, ~loopy-let*~ can be used for
~let~-binding destructured values, similar to ~pcase-let*~.  Because libraries
like =pcase= and =seq= already provide similar destructuring macros using their
own destructuring systems, these macros are not affected by the flags
that configure the destructuring used by loop commands ([[#flags]]), as that would
be redundant.

To be clear, these destructuring macros can be used outside of the looping
macros.

#+begin_src emacs-lisp
  ;; => (5 9)
  (loopy (flag seq)
         ;; `seq-let'-destructuring used by loop command:
         (list (_ &rest cdr) '((key1 . (2 . 3)) (key2 . (4 . 5))))
         ;; Doesn't use `seq-let'-style destructuring:
         (collect (loopy-let* (((a . b) cdr))
                    (+ a b))))
#+end_src

#+findex: loopy-let*
- ~loopy-let*~ :: Use destructuring in a ~let~ form, like in ~pcase-let*~ and
  ~seq-let~.

  #+begin_src emacs-lisp
    ;; => (1 2 3 4 5 28)
    (loopy-let* ((a 1)
                 ([b c] [2 3])
                 ((&keys k1 k2 (k3 28)) '(:k1 4 :k2 5)))
      (list a b c k1 k2 k3))
  #+end_src

#+findex: loopy-setq
- ~loopy-setq~ :: Use destructuring in a ~setq~ form, like in ~seq-setq~ and
  ~pcase-setq~.

  #+begin_src emacs-lisp
    ;; => (1 2 3 4 5 28)
    (let (a b c k1 k2 k3)
      (loopy-setq a 1
                  [b c] [2 3]
                  (&keys k1 k2 (k3 28)) '(:k1 4 :k2 5))
      (list a b c k1 k2 k3))
  #+end_src

#+findex: loopy-lambda
- ~loopy-lambda~ :: Use destructuring in a ~lambda~'s argument list, like in
  ~pcase-lambda~ and ~cl-function~.

  #+begin_src emacs-lisp
    ;; => ((1 2 :k1 3) 110)
    (funcall (loopy-lambda ((&whole first-arg a b &key k1 (k2 4))
                            second-arg)
               (list first-arg (+ a b k1 k2 second-arg)))
             (list 1 2 :k1 3) 100)
  #+end_src

#+findex: loopy-ref
- ~loopy-ref~ :: Create destructured references to the fields in a sequence via
  ~cl-symbol-macrolet~.  Do not confuse this with the behavior of ~cl-letf~,
  which temporarily binds those places to a value.

  This macro uses the destructuring found in the sequence-reference iteration
  commands ([[#sequence-reference-iteration]]).  There are some limitations to this
  functionality in Emacs Lisp, which are described in that section.

  #+begin_src emacs-lisp
    ;; => ((20 2 23) [24 25 26])
    (let ((l1 (list 1 2 3))
          (a1 (vector 4 5 6)))
      (loopy-ref (((a _ b) l1)
                  ([c &rest d] a1))
        (setf a 20
              b 23
              c 24
              d [25 26]))
      (list l1 a1))
  #+end_src

  
* The ~loopy-iter~ Macro
:PROPERTIES:
:CUSTOM_ID: loopy-iter
:DESCRIPTION: Embedding loop commands in arbitrary code.
:END:

#+cindex: loopy-iter
#+findex: loopy-iter
~loopy-iter~ is a macro that allows the embedding of loop commands inside of
arbitrary code.  This is different from the loop command =do=, which allows the
embedding of arbitrary code inside of a ~loopy~ loop.  You must use ~require~ to
load this feature.

This macro is named after the ~iterate~ or ~iter~ macro provided by the Common
Lisp package "Iterate" [fn:iter] (not to be confused with the ~iter-*~ functions
provided by Emacs).  However, while ~loopy~ and ~loopy-iter~ were influenced by
~iterate~, ~loopy-iter~ is not a port of ~iterate~ to Emacs Lisp.

#+begin_src emacs-lisp
  (require 'loopy-iter) ; <- Must `require' to load feature.

  ;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1)
  ;;     (0)
  ;;     (1 2 3 4 5 6 7 8 9 10 11))
  (loopy-iter (accum-opt positives negatives zeroes)
              (numbering i :from -10 :to 10)
              ;; Normal `let' and `pcase', not Loopy constructs:
              (let ((var (1+ i)))
                (pcase var
                  ((pred cl-plusp)  (collecting positives var))
                  ((pred cl-minusp) (collecting negatives var))
                  ((pred zerop)     (collecting zeroes var))))
              (finally-return negatives zeroes positives))

  ;; => (1 2 3)
  (loopy-iter (listing elem '(1 2 3))
              (funcall #'(lambda (x) (collecting x))
                       elem))
#+end_src

#+cindex: loopy-iter name conflicts
The arguments of ~loopy~ are limited to loop commands and special macro
arguments.  ~loopy-iter~ differs by allowing arbitrary Lisp expressions, in
which loop commands are treated as macros to be expanded by ~macroexpand-all~.
Hence, a loop command could overshadow the function value of a symbol.  There
are two ways to avoid such conflicts.

#+cindex: loopy-iter bare names
#+vindex: loopy-iter-bare-names
The first way is to use non-conflicting aliases.  Like in Iterate (and
~cl-loop~, to an extent), almost all commands in ~loopy~ have aliases in the
present-participle form (the "-ing" form).  For example, Loopy provides the
command =list= with the alias =listing=.  Because the command name =list= would
conflict with the built-in Emacs Lisp function ~list~, only the command name
=listing= is supported by default.  These names of commands and special macro
arguments are called {{{dfn(bare names)}}} to distinguish them from the second
way of avoiding conflicts.  The complete list of commands and special macro
arguments that are recognized by default are given in [[#iter-default-names]].

#+begin_src emacs-lisp
  ;; In `loopy', `list' is unambiguously a command name.
  ;; => (1 2 3 4)
  (loopy (named outer)
         (list i '((1 2) (3 4)))
         (loop (list j i)
               (at outer (collect j))))

  ;; In `loopy-iter', `list' would be a function.  `listing' is the command.
  ;; => (1 2 3 4)
  (loopy-iter (named outer)
              (listing i (list (list 1 2) (list 3 4)))
              (loopy-iter (listing j i)
                          ;; Can use `at' instead of `atting':
                          (at outer (collecting j))))
#+end_src

The non-conflicting names of loop commands and special macro arguments
recognized by ~loopy-iter~ can be customized with the user option
~loopy-iter-bare-names~, which is a list of symbols naming commands, special
macro arguments, and their aliases.  Again, these symbols are found in the loop
body by using Emacs Lisp's macro-expansion features, so adding a name that
overrides a symbol's function definition can cause errors. Names that are
obvious conflicts, such as the non-suffixed versions of most loop commands (such
as =list=), loop commands that are unneeded in ~loopy-iter~ (such as =when=),
and the names of Loopy features that are the same as the names of Emacs Lisp
features (such as =let*= for =with=) are by default excluded from
~loopy-iter-bare-names~.

#+cindex: loopy-iter keywords
#+vindex: loopy-iter-keywords
The first method above deals with looping features that are written like
functions.  If for some reason a suitable alias cannot be used or cannot be
added to one of the above user options, one can write the feature name preceded
by one of the keywords in ~loopy-iter-keywords~.  This is the second method.

By default, the possible keywords are =for=, =accum=, =exit=, and =arg=.  These
symbols do not share a name with any built-in Emacs feature and are similar to
the keywords used by other packages.  Note that these are not Lisp "keywords"
that are prefixed with a colon, such as the =:test= in ~(cl-count ITEM SEQ :test
FUNC)~.  For example,

- the command =(listing VAR LIST)= could also be written as =(for list VAR
  LIST)= and as =(for listing VAR LIST)=.
- the special marco argument =with=, with the alias =let*=, could also be
  written as =(arg let* (VAR EXPR) ...)=.  See that this does not conflict with
  using the ~let*~ special form.


This method recognizes all commands and their aliases in the user option
~loopy-parsers~.

#+caption: The first example, but now using keyword symbols.
#+begin_src emacs-lisp
  ;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1)
  ;;     (0)
  ;;     (1 2 3 4 5 6 7 8 9 10 11))
  (loopy-iter (arg accum-opt positives negatives other)
              (for numbers i :from -10 :to 10)
              ;; Normal `let' and `pcase', not Loopy constructs:
              (let ((var (1+ i)))
                (pcase var
                  ((pred cl-plusp)  (accum collect positives var))
                  ((pred cl-minusp) (accum collect negatives var))
                  ((pred zerop)     (accum collect zeroes var))))
              (arg finally-return positives negatives zeroes))
#+end_src

While the symbols =for=, =accum=, =exit=, and =arg= are named for iteration,
accumulation, early exits, and special macro arguments, respectively, any
keyword in the user option ~loopy-iter-keywords~ can be used to identify any
loop command or special macro argument.  For example, =(accum collect VAL)= and
=(for collect VAL)= are both valid ways of referring to the =collect= loop command
in ~loopy-iter~.  Instead of =(arg let* (VAR 7))= in the example above, one could
also write =(exit let* (VAR 7))= if one really wanted to.

While ~loopy-iter~ imposes less restrictions on the contents of the loop body
than the macro ~loopy~, restrictions on the placement of loop commands and
special macro arguments still apply in ~loopy-iter~.  For example, iteration
commands must still occur at the top level of ~loopy-iter~ or a sub-loop.

#+begin_src emacs-lisp
  ;; BAD:
  (loopy-iter (let ((a (progn
                         ;; ERROR: `listing' must occur at the top level.
                         (listing j '(8 9 10 11 12))
                         j)))
                (collecting a)))

  ;; GOOD:
  ;; => (8 9 10 11 12)
  (loopy-iter (let ((a (progn
                         ;; NOTE: No restriction on placement of `setting'.
                         (setting j 8 (1+ j))
                         (when (> j 12) (leaving))
                         j)))
                (collecting a)))
#+end_src

#+cindex: loopy-iter sub-loops
In the macro ~loopy~, the commands =loopy= and =loopy-iter= are needed to
correctly handle sub-loops.  Those commands are not needed in the macro
~loopy-iter~, since the macro expands any macros in its argument while
processing them.

#+begin_src emacs-lisp
  ;; => (2 3 4 5)
  (loopy-iter outer
              (listing i '([1 2] [3 4]))
              ;; NOTE: `loopy-iter' macro, not command
              (loopy-iter (arraying j i)
                          (at outer
                              (let ((val (1+ j)))
                                (collecting val)))))

  ;; => (2 3 4 5)
  (loopy-iter outer
              (listing i '([1 2] [3 4]))
              ;; NOTE: `loopy' macro, not command
              (loopy (array j i)
                     (set val (1+ j))
                     (at outer (collect val))))
#+end_src

Finally, there are a few things to keep in mind when using ~loopy-iter~:

1. You should not rely on the value of a loop command's expanded code.  Such
   expanded code is an implementation detail and subject to change.

2. Some macros, especially those that interact with each other, produce broken
   code while ~loopy-iter~ is expanding its arguments.  For example,
   ~cl-return-from~ is known to be problematic, since it tries to interact with
   the correct ~cl-block~.

   Macros that should not be expanded while ~loopy-iter~ expands are listed in
   ~loopy-iter-suppressed-macros~.  Note that this suppression is only in effect
   while ~loopy-iter~ expands its loop commands.  Once ~loopy-iter~ outputs its
   code, Emacs will attempt to further expand any macros in the outputted code.

   Ideally, such problematic macros are uncommon.  Please report such cases on
   this project's [[https://codeberg.org/okamsn/loopy/issues][issues tracker]] so that they can be added to
   ~loopy-iter-suppressed-macros~ by default.

** Default Bare Names in ~loopy-iter~
:PROPERTIES:
:CUSTOM_ID: iter-default-names
:END:

This section lists the default aliases supported as bare names in the macro
~loopy-iter~.  The list of supported bare names can be customized in the user
options ~loopy-iter-bare-names~.

By default, the following commands are not recognized:
- =do= and =command-do=, which are not needed.

- Commands that exists only as a command version of an existing Lisp feature,
  such as =if=, =cond=, =when=, and =unless=.

- The commands =loopy= and =loopy-iter=.  The macro ~loopy-iter~ expands macros
  in its arguments, and so can properly handle instances of itself and the macro
  ~loopy~ without needing them to be reimplemented as loop commands.

- =while=, as it would conflict with the special form ~while~.
  Use ~(when (not COND) (leaving))~ or ~(unless COND (leaving))~ instead.

- =until=, to be consistent with the exclusion of =while=.


Using the commands =returning= and =returning-from= are the same as using the
macros ~cl-return~ and ~cl-return-from~, except that the commands automatically
create a list if more than one return value is given.

#+begin_src emacs-lisp
  ;; => (6 7 8)
  (loopy-iter (numbering n :from 0 :to 10)
              (when (> n 5)
                (returning n (1+ n) (+ 2 n))))

  ;; => (6 7 8)
  (loopy-iter (numbering n :from 0 :to 10)
              (when (> n 5)
                (cl-return (list n (1+ n) (+ 2 n)))))
#+end_src


- Special Macro Argument Names:
  - =accum-opt=
  - =after-do=
  - =after=
  - =before-do=
  - =before=
  - =else-do=
  - =else=
  - =finally-do=
  - =finally-protect=
  - =finally-protected=
  - =finally-return=
  - =finally=
  - =flag=
  - =flags=
  - =init=
  - =initially-do=
  - =initially=
  - =no-init=
  - =no-with=
  - =opt-accum=
  - =with=
  - =without=
  - =wrap=
- Command Names:
  - Iteration Command Names:
    - Generic Iteration Command Names:
      - =cycling=
      - =repeating=
    - Numeric Iteration Command Names:
      - =numbering=
      - =numbering-down=
      - =numbering-up=
    - Sequence Iteration Command Names:
      - =arraying=
      - =consing=
      - =listing=
      - =mapping-pairs=
      - =mapping=
      - =seqing=
      - =sequencing=
      - =stringing=
    - Sequence Index Iteration Command Names:
      - =arraying-index=
      - =listing-index=
      - =sequencing-index=
      - =seqing-index=
      - =stringing-index=
    - Sequence Reference Iteration Command Names:
      - =arraying-ref=
      - =listing-ref=
      - =mapping-ref=
      - =sequencing-ref=
      - =seqing-ref=
      - =stringing-ref=
  - Accumulation Commands:
    - =accumulating=
    - =adjoining=
    - =appending=
    - =collecting=
    - =concating=
    - =counting=
    - =finding=
    - =maximizing=
    - =minimizing=
    - =multiplying=
    - =nconcing=
    - =nunioning=
    - =prepending=
    - =pushing=
    - =pushing-into=
    - =reducing=
    - =summing=
    - =unioning=
    - =vconcating=
  - Boolean Commands:
    - =always=
    - =never=
    - =thereis=
  - Cycle-Skipping Commands:
    - =continuing=
    - =continuing-from=
    - =skipping=
    - =skipping-from=
  - Early-Exit Commands:
    - =leaving=
    - =leaving-from=
    - =returning=
    - =returning-from=
  - Sub-loop Commands:
    - =at=

* Customizing Macro Behavior

There are three ways to control behavior:

1. Setting a flag ([[#flags]]), which currently controls destructuring
   and whether the generated code uses a ~while~-loop.
2. Modifying the customizable variables like ~loopy-parsers~ and
   ~loopy-iter-bare-names~
3. Using the special macro argument =override=, which modifies the same
   settings as the customizable variables, but only locally for the
   current loop (and not for sub-loops)

A package which extends Loopy and wishes to make those extensions available to
others would modify the customizable variables, similar to defining new
functions and variables in Emacs Lisp's global namespace.  A package which makes
use of Loopy internally and wishes to modify its behavior would use the
=override= special macro argument within a bespoke macro that wraps Loopy.

** Using Flags
:PROPERTIES:
:CUSTOM_ID: flags
:DESCRIPTION: Using flags to change behavior.
:END:

#+cindex: flag
A {{{dfn(flag)}}} is a symbol passed to the =flag= special macro argument,
changing the macro's behavior.  Currently, flags affect what method ~loopy~ uses
to perform destructuring (=pcase=, =seq=, =dash=, or the default method) and
whether ~loopy~ expands with or without the default ~while~-loop.

Flags are applied in order.  If you specify =(flags seq pcase)=, then ~loopy~
will use destructuring like ~pcase-let~, not like ~seq-let~.

The following flags are currently supported:

#+cindex: pcase flag
#+cindex: seq flag
#+cindex: dash flag
#+cindex: no-loop flag
#+cindex: default flag
- =pcase= :: Use ~pcase-let~ for destructuring
  ([[info:elisp#Destructuring with pcase Patterns]]).
- =seq= :: Use ~seq-let~ for destructuring ([[info:elisp#seq-let]]).
- =dash= :: Use the style of destructuring found in the =dash= library
 ([[info:dash#-let]]).
- =no-loop=  :: Expand the looping macros without creating a ~while~-loop.  This
  is intended for wrapping macros that don't want to work around the default
  ~while~-loop.
- =default= :: Use the default behavior for all options.


For convenience, all flags (except =default=) can be undone by prefixing them
with =-= (a dash or minus sign), which reverts ~loopy~ to its default behavior.

These prefixed flags only apply when the unprefixed version is active.  That is,
=(flags pcase -dash)= is the same as just =(flags pcase)=, as =pcase=
destructuring will override all uses of =dash= destructuring as it comes later
in the list.  Similarly, =(flags -dash dash)= and =(flags -dash +dash)= leave
=dash= destructuring enabled, and =(flags +dash -dash)= disables =dash=
destructuring and uses the default behavior.

The flags are described in more detail in the following subsections.

*** Destructuring Flags
:PROPERTIES:
:CUSTOM_ID: destructuring-flags
:DESCRIPTION: Using different destructuring systems.
:END:

There are four flags that change the destructuring system used by Loopy:

- =pcase= :: Use ~pcase-let~ for destructuring
  ([[info:elisp#Destructuring with pcase Patterns]]).
- =seq= :: Use ~seq-let~ for destructuring ([[info:elisp#seq-let]]).
- =dash= :: Use the style of destructuring found in the =dash= library
 ([[info:dash#-let]]).
- =default= :: Use the default behavior for all options.


#+cindex: loopy-dash
#+cindex: loopy-pcase
#+cindex: loopy-seq
The destructuring flags (=pcase=, =seq=, and =dash=) are separate libraries
(respectively, =loopy-pcase=, =loopy-seq=, and =loopy-dash=) that must be
loaded after =loopy=.  Currently, =loopy-dash= is a separate package.

Below are some examples of using the destructuring flags.  These flags affect
the destructuring of:
- iteration variables
- accumulation variables
- variables explicitly named by the commands =set= and =set-prev=
- variables bound by the special macro argument =with=

These flags do not affect the destructuring of generalized variables
(~setf~-able places) as the libraries =pcase.el=, =seq.el=, and =dash.el= do not
yet provide the required functionality.

#+begin_src emacs-lisp
  ;; => ((1 4)            coll1
  ;;     ((2 3) (5 6))    whole
  ;;     (2 5)            x
  ;;     (3 6))           y
  (require 'loopy-dash)
  (loopy (flag dash)
         (list (i j) '((1 (2 3)) (4 (5 6))))
         (collect coll1 i)
         (collect (whole &as x y) j)
         (finally-return coll1 whole x y))

  ;; => ((1 4) (3 6) 10 20 nil nil)
  (require 'loopy-pcase)
  (loopy (flag pcase)
         (with ((or `[,v1 ,v2] `(,v3 ,v4))
                [10 20]))
         (list elem '((1 (2 3)) (4 (5 6))))
         (collect `(,a (,_ ,b)) elem)
         (finally-return a b v1 v2 v3 v4))

  ;; => (14 26)
  (require 'loopy-seq)
  (loopy (flag seq)
         (with ([v1 v2] [10 20]))
         (list (i &rest j) '((1 . 2) (3 . 4)))
         (sum sum1 i)
         (sum sum2 j)
         (finally-return (+ sum1 v1) (+ sum2 v2)))
#+end_src

#+attr_texinfo: :tag Warning
#+begin_quote
For accumulation commands, there is no guarantee that a variable that was used
in destructuring was meant to be user facing.  Destructuring systems can create
new variables as they please, which can be interpreted as accumulation
variables.
#+end_quote

For the warning, consider the below example in which a hypothetical ~pcase~
pattern creates the variable ~temporary?~ for destructuring.  Loopy has no way
of knowing whether it was the user or the destructuring system that created the
variable.  As a result, ~temporary?~ is treated as a user-facing accumulation
variable.  Such cases can be unwanted and produce inefficient code.

#+begin_src emacs-lisp
  ;; Possibly unexpected behavior:
  ;;
  ;; => ((1 2 3) (2 4 6))
  (loopy (flag +pcase)
         (list i '(1 2 3))
         (collect (and whole
                       (let temporary? (* 2 whole)))
                  i)
         (finally-return whole temporary?))
#+end_src

If you wish to always use one of the destructuring flags, you can use a wrapping
macro around ~loopy~ or ~loopy-iter~.  The =flag= special macro argument is
identified by its entry in the customizable variable ~loopy-parsers~.
Therefore, uses of =flag=, including aliases, can be identified by checking
~loopy-parsers~ (so long as the entry for =flag= itself has not been changed).

#+begin_src emacs-lisp
  (require 'loopy-pcase)
  (defmacro my-loopy-flag-wrapper (&rest body)
    "Use `loopy', but default to `pcase' destructuring."
    (loopy (with (entry-for-flag (map-elt loopy-parsers 'flag)))
           (list arg body)
           (if (and (consp arg)
                    (eq (map-elt loopy-parsers (car arg))
                        entry-for-flag))
               (command-do
                (set flag-found t)
                (collect (append arg '(pcase))))
             (collect arg))
           (finally-do
            (unless flag-found
              (push '(flag pcase) loopy-result))
            (push 'loopy loopy-result))))

  ;; => (1 2 3 4)
  (my-loopy-flag-wrapper (list `(,i . ,j) '((1 . 2) (3 . 4)))
                         (collect i)
                         (collect j))

  ;; Ignores the `seq' flag as expected:
  ;;
  ;; => (1 2 3 4)
  (my-loopy-flag-wrapper (flag seq)
                         (list `(,i . ,j) '((1 . 2) (3 . 4)))
                         (collect i)
                         (collect j))
#+end_src


*** The =no-loop= Flag
:PROPERTIES:
:CUSTOM_ID: no-loop-flag
:DESCRIPTION: Avoiding creating the loop.
:END:

Some users of Loopy's macros may prefer using Loopy's non-looping constructs
(such as accumulation commands) inside Emacs Lisp's default looping features
(such as ~dolist~ or ~mapc~).  By default, Loopy creates an infinite
~while~-loop, which can interfere with this approach.

One way of avoiding the infinite ~while~-loop is to use a command like =cycle=
or =leave=, as in the below examples.  This does not prevent the creation of the
~while~-loop; it just causes Loopy to exit the loop after one step.

#+begin_src emacs-lisp
  ;; => 10
  (loopy-iter (cycling 1)
              (dolist (i '(1 2 3 4))
                (summing i)))

  ;; => 10
  (loopy-iter (dolist (i '(1 2 3 4))
                (summing i))
              (leaving))
#+end_src

#+cindex: no-loop flag
Another way to avoid the ~while~-loop is to use the flag =no-loop=.  The benefit
of using the =no-loop= flag is that, because its meaning is clear during macro
expansion, it can trigger additional error checking.  Currently, the =no-loop=
flag will cause Loopy to signal an error when used with the following features:
- Iteration commands like =list= or =array=
- Commands like =skip=

#+begin_src emacs-lisp
  ;; `listing' doesn't make sense with `no-loop', so Loopy
  ;; signals an error here:
  ;;
  (loopy-iter (flag no-loop)
              (listing i '(1 2 3 4))
              (summing i))
#+end_src

Currently, all other Loopy features can be used with the =no-loop= flag,
including:
- Special macro arguments
- Accumulation commands, such as =sum= and =collect=
- Early-exit commands, such as =leave= and =return=
- sub-loop commands

One might wish to use this feature to implement something like ~cl-block~ using
~loopy-iter~, as in the below example.

#+begin_src emacs-lisp
  (defmacro my-loopy-block (&rest args)
    `(loopy-iter (flag no-loop)
                 ,@(butlast args)
                 (finally-return ,(car (last args)))))

  ;; => 6
  (my-loopy-block (dolist (i '(1 2 3))
                    (summing i))
                  loopy-result)

  ;; => 6
  (my-loopy-block (mapc (lambda (i) (summing i))
                        '(1 2 3))
                  loopy-result)
#+end_src

** Custom Aliases
:PROPERTIES:
:CUSTOM_ID: custom-aliases
:DESCRIPTION: How to add one's own aliases.
:END:

#+cindex: custom aliases
An {{{dfn(alias)}}} is another name for a loop command ([[#loop-commands]]) or
special macro argument ([[#macro-arguments]]).  ~loopy~ comes with several built-in
aliases, such as the alias =string= for the command =array= and the alias =else=
for the special macro argument =after-do=.

| Command or Special Macro Argument | Built-In Aliases           |
|-----------------------------------+----------------------------|
| =array=                           | =string=                   |
| =numbers=                         | =number=                   |
| =after-do=                        | =after=, =else=, =else-do= |

Using an alias works the same as using the preferred name of the command or
special macro argument.  Implementation-wise, there is no longer a difference
between aliases and preferred names (which are the ones commonly used and listed
first in this document).  We continue to use the word "alias" for its common
definition rather than to suggest a difference in the code.

#+caption: `array' and `string' are different names for the same command.
#+begin_src emacs-lisp
  ;; => ("a" "b" "c" "d")
  (loopy (array i "abcd")
         (collect (char-to-string i)))

  ;; => ("a" "b" "c" "d")
  (loopy (string i "abcd")
         (collect (char-to-string i)))
#+end_src

#+cindex: aliases in loopy-parsers
Users can define a new name for a command or special macro argument by adding an
entry to the customizable variable ~loopy-parsers~ with the appropriate parsing
function.  Using an alias does not change the fact that special macro arguments
are parsed before loop commands.

#+begin_src emacs-lisp
  (setf (map-elt loopy-parsers 'items) (map-elt loopy-parsers 'array)
        (map-elt loopy-parsers 'as) (map-elt loopy-parsers 'with))

  ;; => (8 9 10)
  (loopy (as (seven 7))
         (items i [1 2 3])
         (collect (+ i seven)))
#+end_src

#+findex: loopy-defalias
Previously, the macro ~loopy-defalias~ was used to modify the now-deprecated
user option ~loopy-aliases~.  Now, it modifies ~loopy-parsers~.  This change
should be unnoticed by users.  ~loopy-defalias~ remains a forward-compatible way
of creating aliases.

#+attr_texinfo: :tag Warning
#+begin_quote
For portability reasons, you should not globally override any of the existing
names, as that might create conflicts during macro expansion with other code
that uses the macro and relies on those names.  Instead, use the
special macro argument =override= ([[#override]]) to create local aliases.
#+end_quote

** Custom Commands
:PROPERTIES:
:CUSTOM_ID: adding-custom-commands
:DESCRIPTION: Extending `loopy' with personal commands.
:END:

This section contains information about how loop commands work and how one can
add custom commands to ~loopy~.  Two examples are provided.

*** Background Info
:PROPERTIES:
:CUSTOM_ID: background-info
:DESCRIPTION: The internals of `loopy'.
:END:

#+cindex: instruction, instructions
The core working of ~loopy~ is taking a loop command and generating code that
becomes part of a ~while~-loop.  This code is represented by
{{{dfn(instructions)}}}, which basically describe where and how code is inserted
into and around a template of a ~while~-loop.

Some examples of instructions are:

- Declaring a given variable in a ~let~-like form to make sure it's locally
  scoped.

- Declaring a generated variable in a ~let~-like form to contain a given value.

- Adding a condition for continuing/exiting the loop.

- Adding code to be run during the main processing section of the ~while~-loop.
  This location is referred to as the {{{dfn(main body)}}} of the loop.

- Adding code to be run after the main processing section, such as for updating
  variables.  This location is referred to as the {{{dfn(latter body)}}} of the
  loop.


For example, parsing the command =(list i '(1 2 3))= produces the following list
of instructions.  Some commands require the creation of unique temporary
variables, such as =list-211= in the below output.

#+BEGIN_SRC emacs-lisp
  ((loopy--iteration-vars (list-211 '(1 2 3)))
   (loopy--iteration-vars (i nil))
   (loopy--pre-conditions (consp list-211))
   (loopy--main-body      (setq i (car list-211)))
   (loopy--latter-body    (setq list-211 (cdr list-211))))
#+END_SRC

The first element of an instruction describes where to insert code into the
template.  The second element of an instruction is the inserted code.  You can
see that not all of the code to be inserted is a valid Lisp form.  For example,
the above instruction referencing ~loopy--iteration-vars~ inserts a binding for
the variable =list-211= into a ~let~-like form.

| Place                   | Code                             |
|-------------------------+----------------------------------|
| =loopy--iteration-vars= | =(list-211 '(1 2 3))=            |
| =loopy--latter-body=    | =(setq list-211 (cdr list-211))= |
| =loopy--pre-conditions= | =(consp list-211)=               |
| =loopy--iteration-vars= | =(i nil)=                        |
| =loopy--main-body=      | =(setq i (car list-211)))=       |

#+cindex: instruction order
Instructions are applied in order in such a way that earlier instructions are
/not/ overridden by later instructions.  For example, if the special macro
argument =with= sets a variable's value, that value will not be overridden by
commands which might try to initialize that variable to ~nil~.  This works
because special macro arguments are always parsed before loop commands.

#+findex: loopy--parse-loop-command
#+findex: loopy--parse-loop-commands
Commands are parsed by ~loopy--parse-loop-command~, which receives a command
call, such as =(list i '(1 2 3))=, and returns a list of instructions.  It does
this by searching for an appropriate command-specific parsing function in
~loopy-parsers~.  For parsing multiple commands in order, there is
~loopy--parse-loop-commands~, which wraps the single-command version.

For example, consider the function ~loopy--parse-if-command~, which parses the
=if= loop command.  It needs to check the instructions of the sub-commands
passed to =if=, looking for code that would be inserted into the loop's main
body (as determined by the first element of the instruction).  Once found, it
wraps that code with an ~if~-form.

#+begin_src emacs-lisp
  ;; Example instructions that could be generated by "set" and "if" commands:
  ;;
  ;; => ((loopy--other-vars (i nil))
  ;;     (loopy--main-body (setq i 1)))
  (loopy--parse-loop-command '(set i 1))

  ;; => ((loopy--other-vars (i nil))
  ;;     (loopy--main-body (if (my-condition)
  ;;                           (setq i 1)
  ;;                         (setq i 2))))
  (loopy--parse-if-command '(if (my-condition)
                                (set i 1)
                              (set i 2)))
#+end_src

For the purpose of this example, below is a version of the parsing function made
of the basic Lisp features with which you are familiar.  The actual definition
makes use of more convenient Emacs Lisp libraries and can be seen in the library
{{{file(loopy-commands.el)}}}.

#+BEGIN_SRC emacs-lisp
  (require 'loopy)

  (defun loopy--parse-if-command (arg)
    "Parse the `if' loop command usage ARG.
  ARG is of the form (if CONDITION IF-TRUE &rest IF-FALSE)."

    (let ((condition (cadr arg))   ; Second element of `arg'.
          (if-true   (caddr arg))  ; Third element of `arg'.
          (if-false  (cdddr arg))) ; Remaining elements of `arg'.

      ;; The main processing of this function is to separate instructions
      ;; for the loop's main body from other instructions,
      ;; and to then wrap those main-body instructions with an
      ;; `if' special form.
      (let ((full-instructions)
            (if-true-main-body)
            (if-false-main-body)
            ;; This variable is just so that iteration commands know when
            ;; they are being used away from the top level of the loop's
            ;; structure (which is an error).
            (loopy--in-sub-level t))

        ;; Process the instructions for the command that should run if the
        ;; condition is true.
        (dolist (instruction (loopy--parse-loop-command if-true))
          (if (eq 'loopy--main-body (car instruction))
              (push (cadr instruction) if-true-main-body)
            (push instruction full-instructions)))

        ;; Process the instructions for the commands that should run
        ;; if the condition is false.
        (dolist (instruction (loopy--parse-loop-commands if-false))
          (if (eq 'loopy--main-body (car instruction))
              (push (cadr instruction) if-false-main-body)
            (push instruction full-instructions)))

        ;; Note: `push' adds elements to the front of a list,
        ;;       so we need to reverse these lists before returning
        ;;       the new list of instructions.

        ;; `loopy--parse-loop-command' always returns a list of instructions.
        ;; For some commands, that means wrapping multiple instructions in
        ;; a `progn' form.  For others, we need to extract the only element.
        (setq if-true-main-body
              (if (= 1 (length if-true-main-body))
                  (car if-true-main-body)
                (cons 'progn (nreverse if-true-main-body))))

        ;; Return the new, full list of instructions.
        `((loopy--main-body (if ,condition
                                ,if-true-main-body
                              ,@(nreverse if-false-main-body)))
          ,@(nreverse full-instructions)))))
#+END_SRC

In general, the code of command parsers is not complicated.  The hardest part of
writing the parser is making sure that the code inserted into the ~while~-loop
template ends up in the correct order.

#+cindex: template variables
A loop command has 7 main places to put code:

#+vindex: loopy--generalized-vars
- =loopy--generalized-vars= :: Lists of a symbol and a macro expansion that will
  be given to =cl-symbol-macrolet=.  This is used to create named ~setf~-able
  places.  The expansion you use depends on the kind of sequence and how the it
  is updated.

  For example, =(list-ref i my-list)= declares =i= to be a symbol which expands
  to =(car TEMP-VAR)=, in which =TEMP-VAR= holds the value of =my-list=.  At the
  end of the loop body, =TEMP-VAR= is set to its =cdr=, ensuring that the next
  call to =car= returns the correct value.

#+vindex: loopy--iteration-vars
- =loopy--iteration-vars= :: Lists of a symbol and an expression that will be
  given to ~let*~.  This is used for initializing variables needed for iteration
  commands, such as the =i= in =(list i '(1 2 3))= or to store the list ='(1 2
  3)= in =(list i '(1 2 3))=.  This also includes variables needed for
  destructuring for said commands.

  Loopy will signal an error if iteration variables would be initialized
  multiple times.  For example, such as if the variable was used by multiple
  iteration commands, that could result in expanding into incorrect code which
  would fail during runtime

#+vindex: loopy--accumulation-vars
- =loopy--accumulation-vars= :: Lists of a symbol and an expression that will be
  given to ~let*~.  This is used for initializing variables needed for
  accumulation commands, such as the =coll= in =(collect coll my-val)= or any
  variables needed for destructuring for said commands.

#+vindex: loopy--other-vars
- =loopy--other-vars= :: Lists of a symbol and an expression that will be
  given to ~let*~.  This is used for initializing variables needed for
  generic commands, such as the =my-var= in =(set my-var 2)= or any
  variables needed for destructuring for said command.

#+vindex: loopy--pre-conditions
- =loopy--pre-conditions= :: Expressions that determine if the =while= loop
  runs/continues, such as whether a list still has elements in it.  If there is
  more than one expression, than all expressions are used in the Emacs Lisp
  special form ~and~.

#+vindex: loopy--main-body
- =loopy--main-body= :: Expressions that make up the main body of the loop.  The
  "main" body of the loop is similar in use to the arguments given to, for
  example, the ~dolist~ macro.  Code that can be placed in the main body
  includes:
  - code that updating some iteration variables, such as the =i= in =(list i
    my-list)=
  - code that updating accumulation variables, such as the =coll= in =(collect
    coll my-value)=
  - code that exits the loop early, such as from the =leave= command

#+vindex: loopy--latter-body
- =loopy--latter-body= :: Expressions that need to be run after the main body,
  such as updating some of the variables that determine when a loop ends.  For
  example, for the command =(list i my-list)=, the command applies the function
  that moves the command through the list (by default, ~cdr~) in the loop's
  latter body, so that during the next iteration of the main body the iteration
  variable =i= can be set to the ~car~ of that ~cdr~.

#+vindex: loopy--post-conditions
- =loopy--post-conditions= :: Expressions that determine whether the =while=
  loop continues, but checked after the loop body has run.  The code from this
  is ultimately appended to the latter body before being substituted in.

For accumulation commands, you might also wish to place values in the following:

#+vindex: loopy--implicit-return
- =loopy--implicit-return= :: A list of values to be returned by the loop if no
  other return value is specified/reached.  A value is added to this list when
  an accumulation command does not specify an accumulation variable, and in some
  special other cases.

  By default, the implicit return value is ~loopy-result~, and so this variable
  is usually just a list of the symbol =loopy-result=.

#+vindex: loopy--vars-final-updates
- =loopy--vars-final-updates= :: Actions to perform on variables (usually
  accumulation variables) after the loop ends.  Some implied accumulation
  commands need to update the variable one final time after ending the loop.
  Some examples are:
  1. Correcting the order of elements in a list that was constructed in reverse.
  2. Coercing the variable into a new sequence type.
  3. Appending a list of lists.

  Each accumulation variable can only be updated once, in a single way.  For
  example, a variable cannot be reversed according to the needs of one command
  and then coerced into a new type according to the needs of another.  Commands
  acting on the same accumulation variable must require the same final update,
  including if they require no final update.


Loopy will attempt to produce efficient code, and will not attempt to set up
features which are not used.  Therefore, the expanded code depends on the kinds
of instructions that are returned by the parsing functions.  For the most part,
the instructions affect the expansion of the loop that contains their respective
command.  However, there are cases where a command must send instructions to a
surrounding loop, not just the loop which immediately contains it.  Consider
using an accumulation command within the =at= command, as in the below example.
The accumulation variable must be declared for the loop =outer=, but the
accumulation itself must still occur within the loop =inner=.

#+begin_src emacs-lisp
  ;; => (1 2 3 4)
  (loopy (named outer)
         (array i [(1 2) (3 4)])
         (loopy inner
                   (list j i)
                   (at outer (collect coll j)))
         (finally-return coll))
#+end_src

To communicate these instructions, use ~loopy--at-instructions~.  For example,
the output of parsing =(at outer (collect j :at start))= would be a list of
instructions similar to those below.  While all of these sub-instructions are
produced by parsing the =collect= command, not all are sent to the loop =outer=.

#+begin_src emacs-lisp
  ;; Example instructions from parsing an `at` command.
  ((loopy--at-instructions
    (outer (loopy--accumulation-vars (loopy-result nil))
           (loopy--implicit-return loopy-result)))
   (loopy--main-body (setq loopy-result (cons j loopy-result))))
#+end_src

#+vindex: loopy--at-instructions
- =loopy--at-instructions= :: Instructions that should be interpreted by a
  surrounding loop.  For example, this kind of instruction is used by the =at=,
  =skip-from=, and =leave-from= commands.  The instruction's value is a list of
  a loop name followed by sub-instructions.

  This variable works as a something like a combination of a stack and a map.
  This means that then when multiple surrounding loops share the same name, the
  instructions affect the innermost surrounding loop of that name.


There are 4 more variables a loop command can push to, but they are derived from
the macro's arguments.  Adding to them after using a macro argument might lead
to unintended behavior.  You might wish to use them if, for example, you are
concerned with what happens after the loop exits/completes.

#+vindex: loopy--before-do
- =loopy--before-do= :: Expressions to evaluate before the loop.  These are
  derived from the =before-do= macro argument.

#+vindex: loopy--after-do
- =loopy--after-do= :: Expressions to evaluate after the loop completes
  successfully.  These are derived from the =after-do= macro argument.

#+vindex: loopy--final-do
- =loopy--final-do= :: Expressions to evaluate after the loop completes,
  regardless of success.  These are derived from the =finally-do= macro
  argument.

#+vindex: loopy--final-return
- =loopy--final-return= :: An expression that is always returned by the macro,
  regardless of any early returns in the loop body.  This is derived from the
  =finally-return= macro argument.


#+vindex: loopy--loop-name
Some commands might depend on the name of the loop.  The symbol which names the
loop is stored in the variable ~loopy--loop-name~.  The default name is ~nil~.

The structure of the macro’s expanded code depends on the features used (for
example, ~loopy~ won’t try to declare variables if none exist), but the result
will work similar to the below example.

#+BEGIN_SRC emacs-lisp
  `(cl-symbol-macrolet ,loopy--generalized-vars
     (let* ,loopy--with-vars
       (let ,loopy--accumulation-vars
         (let* ,loopy--iteration-vars
           (let ((loopy--early-return-capture
                  (cl-block ,loopy--loop-name
                    ,@loopy--before-do
                    (catch loopy--non-returning-exit-tag-name
                      (while ,(cl-case (length loopy--pre-conditions)
                                (0 t)
                                (1 (car loopy--pre-conditions))
                                (t (cons 'and loopy--pre-conditions)))
                        (catch loopy--skip-tag-name
                          ,@loopy--main-body)
                        ,@loopy--latter-body
                        (unless ,loopy--post-conditions
                          (cl-return-from ,loopy--loop-name
                            ,loopy--implicit-return)))
                      ,loopy--vars-final-updates
                      ,@loopy--after-do))
                  ,loopy--implicit-return))
             ,@loopy--final-do
             ,(if loopy--final-return
                  loopy--final-return
                'loopy--early-return-capture))))))
#+END_SRC

*** Hello World
:PROPERTIES:
:CUSTOM_ID: a-small-example
:DESCRIPTION: A minimal working example.
:END:

This is an example of a command that prints a message during the main loop body.

To implement a custom loop-body command, Loopy needs two pieces of information:
1. The keyword that names your command
2. The parsing function that can transform occurences of your command into
   instructions.

Importantly, as with Emacs Lisp functions, there can only be one command with a
given name.

For example, say that you're tired of typing out =(do (message "Hello, %s %s"
PERSONAL-NAME FAMILY-NAME))= and would prefer to instead use =(greet
PERSONAL-NAME [FAMILY-NAME])=.  This only requires adding code to the loop's
main body, so the definition of the parsing function is quite simple.

#+BEGIN_SRC emacs-lisp
  (require 'cl-lib)
  (require 'macroexp)

  (cl-defun my-loopy-greet-command-parser
      ((_ personal-name &optional family-name))
    "Greet one with PERSONAL-NAME and optional FAMILY-NAME."
    ;; Note: Use a generated variable to avoid evaluating `family-name' twice
    ;; (once in the `if' condition and once in the `message').
    ;;
    ;; In newer Emacs, see also `cl-with-gensyms' and `cl-once-only',
    ;; also available from the Compat library
    ;; (https://elpa.gnu.org/packages/compat.html), on which Loopy depends.
    (let ((family-name-var (gensym "family-name")))
      `((loopy--main-body
         (if-let ((,family-name-var ,family-name))
             (message "Hello, %s %s" ,personal-name ,family-name-var)
           (message "Hello, %s" ,personal-name))))))
#+END_SRC

Loopy will pass the entire command expression to the parsing function, and
expects that a list of instructions will be returned.

#+vindex: loopy-parsers
To tell Loopy about this function, add it and the command name =greet= to the
variable ~loopy-parsers~, which associates commands with parsing
functions.  The function that is paired with the symbol receives the entire
command expression, and should produce a list of valid instructions.

#+BEGIN_SRC emacs-lisp
  ;; Using the Map library, for convenience:
  (require 'map)
  (setf (map-elt loopy-parsers 'greet)
        #'my-loopy-greet-command-parser)
#+END_SRC

After that, you can use your custom command in the loop body.

#+BEGIN_SRC emacs-lisp
  (loopy (list name '(("John" "Deer") ("Jane" "Doe") ("Jimmy")))
         (greet (car name) (cadr name)))
#+END_SRC

By running {{{kbd(M-x pp-macroexpand-last-sexp RET)}}} on the above expression,
you can see that it expands to do what we want, as expected.

#+BEGIN_SRC emacs-lisp
  ;; An example expansion:
  (let* ((list-166 '(("John" "Deer") ("Jane" "Doe") ("Jimmy")))
         (name nil))
    (cl-block nil
      (while (consp list-166)
        (setq name (car list-166))
        (if-let ((family-name900 (cadr name)))
            (message "Hello, %s %s"
                     (car name)
                     family-name900)
          (message "Hello, %s"
                   (car name)))
        (setq list-166 (cdr list-166)))
      nil))
#+END_SRC

*** An ~always~ Command
:PROPERTIES:
:CUSTOM_ID: always-example
:DESCRIPTION: Adding a feature from `cl-loop'.
:END:

Lets say we want to emulate ~cl-loop~'s =always= clause, which causes the loop
to return ~nil~ if an expression evaluates to ~nil~ and ~t~ otherwise.  This is
similar to the functions ~cl-every~ and ~seq-every-p~.

Here is an example:

#+BEGIN_SRC emacs-lisp
  ;; => t
  (cl-loop for i from 1 to 9 always (< i 10))
#+END_SRC

While ~loopy~ already has an =always= command, we'll ignore it for the sake of
this example.  Without a custom command, you could translate this using the
following code:

#+BEGIN_SRC emacs-lisp
  ;; => t
  (loopy (numbers i :from 1 :to 9)
         (unless (< i 10) (return nil))
         (else-do (cl-return t)))
#+END_SRC

This is similar to what you might write in other languages, such as Python.

#+begin_src python
  # In some testing Python function:
  for i in range(1, 10):
      if not (i < 10):
          return False
  else:
      return True
#+end_src

While the meaning of the code is clear, this approach is certainly wordier.

Here's how one could do this using a custom command.  Again, Loopy already
comes with a built-in =always= command.  This example is taken directly from the
file {{{file(loopy-commands.el)}}}, which contains the code of all of ~loopy~'s
built-in parsers.

We can describe the command's desired behavior in two sentences:
1. The loop should immediately return ~nil~ if the expression ever evaluates to
   ~nil~.
2. The loop should return ~t~ if the loop is able to complete successfully.

This simplest way to satisfy the first requirement is to conditionally use
~cl-return~ if the expressions ever evaluates to ~nil~.  We want to do this
while the loop is running, so we should use an instruction for
~loopy--main-body~.

#+begin_src emacs-lisp
  ;; We want to insert something like the below code into the loop
  (unless CONDITION
    (cl-return nil))

  ;; so we could use the instruction
  `(loopy--main-body (unless ,CONDITION (cl-return nil)))

  ;; in which CONDITION is supplied by the parsing function.
#+end_src

For a simple loop, this works well enough.  However, this approach has two
problems:
1. If the loop is named something other than =nil=, ~cl-return~ would return
   from the wrong ~cl-block~.
2. Using ~cl-return~ directly causes the loop to exit immediately without
   finalizing optimized accumulation values, so we wouldn't be able to correctly
   return multiple values from the macro.

The first problem could be solved by using the variable ~loopy--loop-name~ to
retrieve the symbol naming the current loop, but that still leaves the second
problem.  The better solution, when the condition is no longer meant, is to
store a ~nil~ value in an accumulation command and to then leave the loop while
still finalizing variables, such as via the =leave= command.  This gives us
something like:

#+begin_src emacs-lisp
  `(loopy--main-body (unless ,CONDITION
                       (setq ,ACCUM-VAR nil)
                       ,@(LEAVE-COMMAND-CODE)))
#+end_src

The best way to satisfy the second requirement is to set the initial value of
the accumulation variable to ~t~ and to make that accumulation variable the
implied return value of the loop.  As described in [[#exiting-the-loop-early]],
leaving a loopy early (such as via the =leave= command) does not change the
implied return value of a loop.  The logic of the command would be:
1. Initialize the accumulation variable to ~t~.
2. During the loop, if the condition is ~nil~, immediately set the accumulation
   variable to ~nil~ and leave the loop.  If the condition is always non-~nil~,
   then we need do nothing.
3. Regardless of what happens during the loop, the implied return value is used
   as the return value of the macro, barring any use of the =finally-return=
   macro argument.

With that in mind, our instructions for the loop would be

#+begin_src emacs-lisp
  `((loopy--accumulation-vars (,ACCUM-VAR t))
    (loopy--implicit-return ,ACCUM-VAR)
    (loopy--main-body (unless ,COND
                        (setq ,ACCUM-VAR nil)
                        ,@(LEAVE-COMMAND-CODE))))
#+end_src

Once we've chosen our instructions, we need to tell Loopy what function to use
to produce these instructions.  Like in the previous example, we define the
parsing function and add it to ~loopy-parsers~.

#+begin_src emacs-lisp
  ;; As noted in the previous section, the parsing function is always
  ;; passed the entire command as `(always CONDTION)', not just the
  ;; command arguments as `CONDITION'.
  (require 'cl-lib)
  (require 'map)
  (require 'seq)
  (require 'loopy)

  (cl-defun my--loopy-always-command-parser ((_ condition))
    "Parse a command of the form `(always CONDITION)'.

  If any condition is nil, `loopy' should immediately return nil.
  Otherwise, `loopy' should return t."
    (let ((result-var 'loopy-result))
      `((loopy--accumulation-vars (,result-var t))
        (loopy--implicit-return ,result-var)
        ;; Get the main-body expressions of `leave' and splice
        ;; into the `unless' body.  The other `leave' instructions
        ;; are unmodified and included in the output of the parser.
        ,@(loopy (accum-opt main-exprs other-instrs)
                 (list (&whole instr place expr) (loopy--parse-leave-command '(leave)))
                 (if (eq place 'loopy--main-body)
                     (collect main-exprs expr)
                   (collect other-instrs instr))
                 (finally-return
                  `(,@other-instrs
                    (loopy--main-body (unless ,condition
                                        (setq ,result-var nil)
                                        ,@main-exprs))))))))

  (setf (map-elt loopy-parsers 'always)
        #'my--loopy-always-command-parser)
#+end_src

Once we've added our parsing function to ~loopy-parsers~, Loopy will use that
function whenever it tries to understand the =always= command.  In this case,
this custom parser would supercede the built-in parser.  An example output of
parsing an example command would be:

#+begin_src emacs-lisp
  (my--loopy-always-command-parser '(always (< i 10)))

  ;; =>
  ((loopy--accumulation-vars (loopy-result t))
   (loopy--implicit-return loopy-result)
   (loopy--non-returning-exit-used loopy--non-returning-exit-tag)
   (loopy--main-body (unless (< i 10)
                       (setq loopy-result nil)
                       (throw 'loopy--non-returning-exit-tag t))))
#+end_src

Here are some examples of the command in action:

#+begin_src emacs-lisp
  ;; One condition: => t
  (loopy (list i (number-sequence 1 9)) (always (< i 10)))

  ;; Two conditions: => nil
  (loopy (list i (number-sequence 1 9))
         (list j '(2 4 6 8 9))
         (always (< i 10) (cl-evenp j)))

  ;; The previous example is equivalent to this.
  (loopy (list i (number-sequence 1 9))
         (list j '(2 4 6 8 9))
         (always (and (< i 10) (cl-evenp j))))
#+end_src

The actual definition of the =always= command can be read in the library
{{{file(loopy-commands.el)}}}.  It is close to the above definition, but
includes more error checking for working with the other commands.


*** Custom commands in the ~loopy-iter~ macro
:PROPERTIES:
:CUSTOM_ID: custom-commands-for-loopy-iter
:END:

See also [[#loopy-iter]].

~loopy-iter~ works by:
1. separating main-body instructions from other instructions
2. expanding that main-body code for ~loopy~ sub-commands
3. collected those expansions into the outputted loop's body
4. processing the collected non-main-body instructions

Most loop commands should work with ~loopy-iter~ without any changes.  Some loop
commands are just ~loopy~ versions of built-in Lisp macros or special forms, and
so aren't needed in ~loopy-iter~ in the first place.

However, special consideration is needed for commands that produce main-body
code outside of main-body instructions, or that assume all of their arguments
are loop commands.  Consider an example definition of the parser for the =at=
command:

#+begin_src emacs-lisp
  (cl-defun loopy--parse-at-command ((_ target-loop &rest commands))
    "Parse the `at' command as (at &rest COMMANDS).

  These commands affect other loops higher up in the call list."
    (loopy--check-target-loop-name target-loop)
    (let ((loopy--loop-name target-loop)
          (loopy--in-sub-level t))
      `((loopy--at-instructions
         (,target-loop
          ,@(loopy--parse-loop-commands commands))))))
#+end_src

All it does is process its arguments and wrap the resulting instructions in an
=at= instruction.  By wrapping main-body instructions in an =at= instruction,
~loopy-iter~ does not see the main-body code, and so cannot use it.  Instead, an
alternative parser must be used:

#+begin_src emacs-lisp
  (cl-defun loopy-iter--parse-at-command ((_ target-loop &rest commands))
    "Parse the `at' command as (at &rest COMMANDS).

  These commands affect other loops higher up in the call list."
    (loopy--check-target-loop-name target-loop)
    ;; We need to capture all non-main-body instructions into a new `at'
    ;; instruction, so we just temporarily `let'-bind
    ;; `loopy-iter--non-main-body-instructions' while the expanding functions push
    ;; to it, which we then wrap back in a new instruction and pass up to the
    ;; calling function, which consumes instructions.
    (loopy (with (loopy-iter--non-main-body-instructions nil)
                 (loopy--loop-name target-loop)
                 (loopy--in-sub-level t)
                 (loopy-iter--level (1+ loopy-iter--level)))
           (list cmd commands)
           (collect (list 'loopy--main-body (macroexpand-all
                                             cmd
                                             macroexpand-all-environment)))
           (finally-return
            ;; Return list of instructions to comply with expectations of calling
            ;; function, which thinks that this is a normal loop-command parser.
            `(,@loopy-result
              (loopy--at-instructions
               (,target-loop
                ,@(thread-last loopy-iter--non-main-body-instructions
                               nreverse
                               (apply #'append))))))))
#+end_src

#+vindex: loopy-iter--non-main-body-instructions
This ~loopy-iter~-specific definition separates the main-body instructions out
from the other instructions so that ~loopy-iter~ can operate on their values
directly.  The variable ~loopy-iter--non-main-body-instructions~ is a place
where the macro-expanding parsers used by ~loopy-iter~ can push lists of
instructions (see the code for usage examples).

#+vindex: loopy-iter-overwritten-command-parsers
These alternative command parsers are listed in
~loopy-iter-overwritten-command-parsers~.

*** Finding More Examples
:PROPERTIES:
:CUSTOM_ID: finding-more-examples
:END:

If you would like to see more examples, consider reading through the source code
of {{{file(loopy-commands.el)}}}, which contains the code of all of the built-in
loop commands.  You can easily find this file using {{{kbd(M-x find-library
loopy-commands RET)}}}.

** Locally Overriding Behavior
:PROPERTIES:
:CUSTOM_ID: override
:DESCRIPTION: Using the `override' special macro argument.
:END:

The special macro argument =override= is used to override the global values of
the customizable variables.  This is desirable when one wants to configure
Loopy's behavior without affecting other libraries that might be using Loopy.
For example, consider namespace collisions: a library which makes a custom
loop command available via ~loopy-command-parsers~ might
overwrite another definition created by some other, unknown library.  This is
similar to what would happen if different libraries tried to use the same
function name or variable name.  If the loop command is only meant to be used
internally, then local overrides paired with wrapping macros can help to avoid
the problem of unintentionally affecting other libraries.

The syntax of the special macro argument =override= is similar to that of the
special macro argument =with=.  Note, however, its arguments are not evaluated,
but taken literally, similar to normal macro arguments.  As shown in the below
example, this is not an issue when used with a wrapping macro, because any
needed evaluation can occur during the execution of the wrapper.

#+begin_src emacs-lisp
  (defvar my-pkg--desired-aliases '((each . seq)
                                    (vect . array))
    "New names for built-in commands.")

  (defmacro my-pkg--loopy (&rest args)
    `(loopy (override (loopy-parsers
                       ,(loopy
                         (with (parsers (map-copy loopy-parsers)))
                         (map (k . v) my-pkg--desired-aliases)
                         (do (setf (map-elt parsers k) (map-elt parsers v)))
                         (finally-return parsers))))
            ,@args))

  (my-pkg--loopy (each i some-seq)
                 (vect j some-array)
                 (collect (cons i j)))
#+end_src

Overrides use the name of the overridden variable.  Overrides replace the global
definitions of variables for a particular use of a macro.  They cannot be used
to remove entries from variable values.  They do not affect any sub-uses of the
macro.  The following variables can be overridden:

- ~loopy-parsers~ :: A =map.el=-compatible structure that maps a symbol to a
  parsing function.  Parsing functions must be available during macro expansion.
  See the user option ~loopy-parsers~ and see [[#adding-custom-commands]].

  In both ~loopy~ and ~loopy-iter~, overriding ~loopy-parsers~ will also
  override any effects from setting the deprecated variables
  ~loopy-command-parsers~ and ~loopy-aliases~ to a non-~nil~ value.

  #+begin_src emacs-lisp
    (cl-defun my-set-parser ((_name var val))
      `((loopy--other-vars (,var nil))
        (loopy--main-body (setq ,var ,val))))

    (defmacro my-loopy-wrapper (&rest args)
      (let ((map (map-insert loopy-parsers 'my-set #'my-set-parser)))
        `(loopy (override (loopy-parsers ,map))
                ,@args)))

    ;; => 3
    (my-loopy-wrapper (my-set i 3)
                      (return i))
  #+end_src

  Overriding ~loopy-parsers~ in ~loopy-iter~ will also override the effects of
  any settings in ~loopy-iter-overwritten-parsers~.  Currently, there is no
  override for ~loopy-iter-overwritten-parsers~.  Using the override for
  ~loopy-parsers~ achieves the same effect.

  #+begin_src emacs-lisp
    ;; The same example as above, but for `loopy-iter':
    (defmacro my-loopy-iter-wrapper (&rest args)
      (let ((map (map-merge '(hash-table :test eq)
                            loopy-parsers
                            loopy-iter-overwritten-parsers
                            '((my-set . my-set-parser))))
            (list (cons 'my-set loopy-iter-bare-names)))
        `(loopy-iter (override (loopy-iter-bare-names ,list)
                               (loopy-parsers ,map))
                     ,@args)))
  #+end_src

- ~loopy-iter-bare-names~ :: A list of symbols.  See the user option
  ~loopy-iter-bare-names~ and see [[#loopy-iter]].

  #+begin_src emacs-lisp
    (cl-defun my-set-parser ((_name var val))
      `((loopy--other-vars (,var nil))
        (loopy--main-body (setq ,var ,val))))

    (defmacro my-loopy-iter-wrapper (&rest args)
      (let ((map (map-insert loopy-parsers 'my-set #'my-set-parser))
            (list (cons 'my-set loopy-iter-bare-names)))
        `(loopy-iter (override (loopy-iter-bare-names ,list)
                                (loopy-command-parsers ,map))
                     ,@args)))

    ;; => 3
    (my-loopy-iter-wrapper (my-set i 3)
                           (return i))
  #+end_src

- ~loopy-iter-keywords~ :: A list of symbols.  See the user option
  ~loopy-iter-keywords~ and see [[#loopy-iter]].

  #+begin_src emacs-lisp
    (defmacro my-loopy-iter-wrapper (&rest args)
      (let ((list (cons 'my-keyword loopy-iter-keywords)))
        `(loopy-iter (override (loopy-iter-keywords ,list))
                     ,@args)))

    ;; => (1 2 3)
    (my-loopy-iter-wrapper (my-keyword list i '(1 2 3))
                           (collecting i))
  #+end_src



* Comparing to ~cl-loop~
:PROPERTIES:
:CUSTOM_ID: comparing-to-cl-loop
:DESCRIPTION: Why `loopy' instead of `cl-loop'.
:END:

~loopy~ is a better version of ~cl-loop~, judging by the following:

- It is generally as fast or faster than ~cl-loop~, though some optimizations
  are more explicit.

- Loop commands are always evaluated in order.

- ~loopy~ provides a more complete destructuring system, which can be swapped out
  with others.

  #+begin_src emacs-lisp
    ;; No array destructuring in `cl-loop'.
    ;; => (3 7)
    (loopy (list [i j] '([1 2] [3 4]))
           (collect (+ i j)))
  #+end_src

- ~loopy~ provides more looping constructs out of the box with a (according to
  the author) clearer naming scheme than ~cl-loop~'s =for= clauses.

  #+begin_src emacs-lisp
    (loopy (list i my-var1)
           (array j my-var2)
           (collect (cons i j)))

    (cl-loop for i in my-var1
             for j across my-var2
             collect (cons i j))
  #+end_src

- Taking influence from Common Lisp's Iterate, several ~loopy~ commands are more
  featureful than their ~cl-loop~ counterparts.

  #+begin_src emacs-lisp
    ;; `array' is more featureful than `for-across'.
    ;; => ((9 . 20) (7 . 18) (5 . 16) (3 . 14) (1 . 12))
    (loopy (array i [11 12 13 14 15 16 17 18 19 20]
                  :downfrom 9 :by 2 :to 1 :index idx)
           (collect (cons idx i)))
  #+end_src

- ~loopy~ provides more control over what happens after and around the loop.
  ~cl-loop~ lacks equivalents of the following special macro arguments:
  - =after-do=: There is only a ~finally do~.
  - =without=: There is no way to prevent ~cl-loop~ from binding variables.
  - =finally-protect=: Though niche, ~loopy~ can wrap only the loop body in
    ~unwind-protect~.
  - =accum-opt=: Named accumulations are always more slow than implicit
    accumulations in ~cl-loop~, even when they don't need to be.
  - =wrap=: Again niche, but missing from ~cl-loop~.

- ~loopy~ is more extensible.  Loop commands can be added easily.

- When using ~loopy-iter~, the macro is more flexible in how it can be used.

* Translating to and from ~cl-loop~
:PROPERTIES:
:CUSTOM_ID: translating-from-cl-loop
:DESCRIPTION: Converting `cl-loop' to `loopy', and vice versa.
:END:

~loopy~ and ~cl-loop~ use slightly different terminology.  The equivalent of
"for clauses" are referred to as "iteration commands" in ~loopy~, as they are
generally used for iterating through sequences.  Meanwhile, "iteration clauses"
can be separated into "iteration commands" (=list= and =array=) and "early-exit
commands" (=while= and =until=).

"Accumulation clauses" work the same as "accumulation commands".

** For Clauses
:PROPERTIES:
:CUSTOM_ID: for-clauses
:END:

As Emacs has many functions that return lists, there is no need to implement an
exact equivalent for every =for=-clause that ~cl-loop~ has.  Instead, one can
just iterate through the return value of the appropriate function using the
=list= command.

For the commands operating on hash tables, see also the generic iteration
command =map-pairs=, which works generically on hash tables, association lists
("alists"), property lists ("plists"), and vectors.

| ~cl-loop~                                     | ~loopy~                                                         |
|-----------------------------------------------+-----------------------------------------------------------------|
| =for VAR from EXPR1 to EXPR2 by EXPR3=        | =(numbers VAR :from EXPR1 :to EXPR2 :by EXPR3)=                 |
| =for VAR in LIST [by FUNCTION]=               | =(list VAR LIST :by FUNC)=                                      |
| =for VAR on LIST [by FUNCTION]=               | =(cons VAR VAL :by FUNC)=                                       |
| =for VAR in-ref LIST by FUNCTION=             | =(list-ref VAR LIST :by FUNC)=                                  |
| =for VAR across ARRAY=                        | =(array VAR ARRAY)=                                             |
| =for VAR across-ref ARRAY=                    | =(array-ref VAR ARRAY)=                                         |
| =for VAR being the elements of SEQUENCE=      | =(sequence VAR SEQUENCE)=                                       |
| =for VAR being the elements of-ref SEQUENCE=  | =(sequence-ref VAR SEQUENCE)=                                   |
| =for VAR being the symbols [of OBARRAY]=      | None so far.  Use ~mapatoms~.                                   |
| =for VAR being the hash-keys of HASH-TABLE=   | =(list VAR (hash-table-keys HASH-TABLE))=                       |
| =for VAR being the hash-values of HASH-TABLE= | =(list VAR (hash-table-values HASH-TABLE))=                     |
| =for VAR being the key-codes of KEYMAP=       | None so far.  Use ~map-keymap~.                                 |
| =for VAR being the key-bindings of KEYMAP=    | None so far.  Use ~map-keymap~.                                 |
| =for VAR being the key-seqs of KEYMAP=        | None so far.                                                    |
| =for VAR being the overlays [of BUFFER]=      | =(list VAR (with-current-buffer BUFFER (car (overlay-lists))))= |
| =for VAR being the intervals [of BUFFER]=     | None so far.                                                    |
| =for VAR being the frames=                    | =(list VAR (frame-list))=                                       |
| =for VAR being the windows [of FRAME]=        | =(list VAR (window-list FRAME))=                                |
| =for VAR being the buffers=                   | =(list VAR (buffer-list))=                                      |
| =for VAR = EXPR1 then EXPR2=                  | =(set VAR EXPR1 EXPR2)=                                         |

** Iteration Clauses
:PROPERTIES:
:CUSTOM_ID: iteration-clauses
:END:

| ~cl-loop~          | ~loopy~           |
|--------------------+-------------------|
| =repeat INT=       | =(cycle INT)=     |
| =while COND=       | =(while COND)=    |
| =until COND=       | =(until COND)=    |
| =iter-by iterator= | =(iter ITERATOR)= |
| =never=            | =(never COND)=    |
| =always=           | =(always COND)=   |
| =thereis=          | =(thereis COND)=  |

** Accumulation Clauses
:PROPERTIES:
:CUSTOM_ID: accumulation-clauses
:END:

Like with ~cl-loop~, in ~loopy~, accumulation commands accumulate into the same
variable when no =VAR= is given (by default, ~loopy-result~).

| ~cl-loop~                | ~loopy~              |
|--------------------------+----------------------|
| =append EXPR into VAR=   | =(append VAR EXPR)=  |
| =collect EXPR into VAR=  | =(collect VAR EXPR)= |
| =concat EXPR into VAR=   | =(concat VAR EXPR)=  |
| =count EXPR into VAR=    | =(count VAR EXPR)=   |
| =maximize EXPR into VAR= | =(maximize VAR EXPR)= |
| =minimize EXPR into VAR= | =(minimize VAR EXPR)= |
| =nconc EXPR into VAR=    | =(nconc VAR EXPR)=   |
| =sum EXPR into VAR=      | =(sum VAR EXPR)=     |
| =vconcat EXPR into VAR=  | =(vconcat VAR EXPR)= |

** Other Clauses
:PROPERTIES:
:CUSTOM_ID: other-clauses
:END:

In ~loopy~, =if=, =when=, and =unless= can take multiple loop commands as
arguments, and operate more like their Lisp counterparts.

This means that =if= is not a synonym for =when=.  Just like the normal Lisp
special form ~if~, =(if COND cmd1 cmd2 cmd3)= only runs =cmd1= if =COND=
evaluates to non-nil, and only runs commands =cmd2= and =cmd3= if =COND=
evaluates to ~nil~.

~loopy~ also provides the command =cond=, which works like the normal Lisp
special form ~cond~.

| ~cl-loop~              | ~loopy~                                       |
|------------------------+-----------------------------------------------|
| =with var = value=     | =(with (VAR VALUE))= as a macro argument      |
| =if COND clause=       | =(if COND CMDS)= as a loop command            |
| =when COND clause=     | =(when COND CMDS)= as a loop command          |
| =unless COND clause=   | =(unless COND CMDS)= as a loop command        |
| =named NAME=           | =NAME= or =(named NAME)= as a macro argument  |
| =initially [do] EXPRS= | =(before-do EXPRS)= as a macro argument       |
| =finally [do] EXPRS=   | =(finally-do EXPRS)= as a macro argument      |
| =finally return EXPR=  | =(finally-return EXPR)= as a macro argument   |
| =do EXPRS=             | =(do EXPRS)= as a loop command                |
| =return EXPR=          | =(return EXPR)= as a loop command             |

* Index of Concepts
  :PROPERTIES:
  :INDEX: cp
  :END:

* Index of Variables
  :PROPERTIES:
  :INDEX: vr
  :END:

* Index of Functions and Macros
  :PROPERTIES:
  :INDEX: fn
  :END:

* Index of Special Macro Arguments
  :PROPERTIES:
  :INDEX: pg
  :END:

* Index of Loop Commands
  :PROPERTIES:
  :INDEX: tp
  :END:

* Footnotes
[fn:dash] https://github.com/magnars/dash.el

[fn:1] Strings being a kind of array.  See [[info:elisp#Sequences Arrays Vectors]]
for more.

[fn:iter] https://common-lisp.net/project/iterate/

[fn:repo] [[https://codeberg.org/okamsn/loopy]]

# Local Variables:
# toc-org-max-depth: 20
# sentence-end-double-space: t
# org-adapt-indentation: nil
# End:

#  LocalWords:  alists plists Loopy's CL's
