\input texinfo    @c -*- texinfo -*-
@c %**start of header
@setfilename loopy.info
@settitle Loopy: A Looping and Iteration Macro
@documentencoding UTF-8
@documentlanguage en
@c %**end of header

@dircategory Emacs
@direntry
* Loopy: (loopy).       A better looping and iteration macro.
@end direntry

@finalout
@titlepage
@title Loopy: A Looping and Iteration Macro
@end titlepage

@contents

@ifnottex
@node Top
@top Loopy: A Looping and Iteration Macro

@code{loopy} is a macro meant for iterating and looping.  It is similar in usage to
@code{cl-loop} (@ref{Loop Facility,,,cl,}) but uses parenthesized expressions rather than
keyword clauses.

For most cases@comma{} @code{loopy} is a featureful replacement for @code{cl-loop} and
complementary to Emacs's built-in looping and mapping features (such as the
libraries @samp{seq} (@ref{Sequence Functions,,,elisp,}) and @samp{cl-lib} (@ref{Top,,,cl,})).


@end ifnottex

@menu
* Introduction::                 A short overview of features.
* Basic Concepts::               Basic information about `loopy' and its loops.
* Special Macro Arguments::      Creating the environment of the loop.
* Loop Commands::                The main features of `loopy'.
* Destructuring Macros::         Destructuring outside of the loop.
* The @code{loopy-iter} Macro::  Embedding loop commands in arbitrary code.
* Customizing Macro Behavior::
* Comparing to @code{cl-loop}::  Why `loopy' instead of `cl-loop'.
* Translating to and from @code{cl-loop}:: Converting `cl-loop' to `loopy', and vice versa.
* Index of Concepts::
* Index of Variables::
* Index of Functions and Macros::
* Index of Special Macro Arguments::
* Index of Loop Commands::

@detailmenu
--- The Detailed Node Listing ---

Loop Commands

* Basic Destructuring::          How to destructure variables and values in loop commands.
* Generic Evaluation::           Setting variables, evaluating expressions, etc.
* Iteration::                    Iterating through sequences, etc.
* Accumulation::                 Accumulating values into new sequences, aggregating values, etc.
* Checking Conditions::          Testing whether a condition holds true.
* Control Flow::                 When to run loop commands.
* Sub-Loops::                    Running a loop within a loop.

Iteration

* Generic Iteration::            Looping a certain number of times.
* Numeric Iteration::            Iterating through numbers.
* Sequence Iteration::           Iterating through sequences.
* Sequence Index Iteration::     Iterating through indices without accessing values.
* Sequence Reference Iteration:: Iterating through places/fields in sequences.

Accumulation

* Common Properties and Arguments of Accumulation Commands::
* Generic Accumulation::         Accumulating function output.
* Numeric Accumulation::         Using and returning numbers.
* Sequence Accumulation::        Using and returning sequences.
* Other Accumulation Commands::
* Optimizing Accumulations::     Producing efficient accumulations.

Control Flow

* Conditional Execution::        Choosing if commands should run.
* Skipping Cycles::              Immediately beginning the next iteration.
* Early Exit::                   Leaving the loop early, with or without returning values.

The @code{loopy-iter} Macro

* Default Bare Names in @code{loopy-iter}::

Customizing Macro Behavior

* Using Flags::                  Using flags to change behavior.
* Custom Aliases::               How to add one's own aliases.
* Custom Commands::              Extending `loopy' with personal commands.
* Locally Overriding Behavior::  Using the `override' special macro argument.

Using Flags

* Destructuring Flags::          Using different destructuring systems.
* The @samp{no-loop} Flag::      Avoiding creating the loop.

Custom Commands

* Background Info::              The internals of `loopy'.
* Hello World::                  A minimal working example.
* An @code{always} Command::     Adding a feature from `cl-loop'.
* Custom commands in the @code{loopy-iter} macro::
* Finding More Examples::

Translating to and from @code{cl-loop}

* For Clauses::
* Iteration Clauses::
* Accumulation Clauses::
* Other Clauses::

@end detailmenu
@end menu

@node Introduction
@chapter Introduction

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

@findex loopy
@table @asis
@item @code{loopy}
A macro similar to @code{cl-loop}.  Unlike @code{cl-loop}@comma{} @code{loopy} uses
parenthetical forms instead of keyword ``clauses''.

@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 lisp
@end table

@findex loopy-iter
@table @asis
@item @code{loopy-iter}
A macro similar to Common Lisp's Iterate macro (@ref{The @code{loopy-iter} Macro}).
Unlike Common Lisp's @code{loop}@comma{} the @code{iterate} macro allows one to embed its
looping constructs inside of arbitrary code.  To be clear@comma{} @code{loopy-iter} is
not a port of @code{iterate} to Emacs Lisp.

@lisp
;; => ((1 2 3) (-3 -2 -1) (0))
(loopy-iter (numbering i -3 3)
            ;; Normal `pcase' macro@comma{} 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 lisp
@end table

@findex loopy-let
@findex loopy-setq
@findex loopy-lambda
@findex loopy-ref
@table @asis
@item @code{loopy-let*}@comma{} @code{loopy-setq}@comma{} @code{loopy-lambda}@comma{} and @code{loopy-ref}
Destructuring
macros that can be used outside of @code{loopy} and @code{loopy-iter}
(@ref{Basic Destructuring}).  For convenience@comma{} Loopy provides its own form of
destructuring@comma{} which is similar to@comma{} though more featureful than@comma{} that provided
by @samp{cl-lib}.

@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 lisp

@item A @code{pcase} pattern
Loopy provides a @code{pcase} pattern@comma{} called @samp{loopy}@comma{} which
can be used in macros like @code{pcase-lambda} and @code{pcase-let}.  Some of the
destructuring macros provided by Loopy are implemented using this @code{pcase}
macro.

@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 lisp
@end table


Some other things to note are:

@itemize
@item
@code{loopy} (and so @code{loopy-iter}) supports destructuring for both iteration and
accumulation commands.

@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 lisp

@item
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@comma{} such as by using an alternative destructuring system
like @samp{pcase} or the Dash library.

@lisp
;; Use `pcase' to destructure array elements:
;; => ((1 2 3 4) (10 12 14) (11 13 15))
(loopy (flag pcase)
       (array (or `(@comma{}car . @comma{}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 lisp

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

@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 lisp

@item
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@comma{} one can make Loopy
optimize for at-start accumulations or at-end accumulations.

@lisp
;; Expands into the efficient `push'-`nreverse' idiom@comma{} 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 lisp
@end itemize


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

@node Basic Concepts
@chapter Basic Concepts

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

For convenience and clarity@comma{} expressions that generate code in the loop body are
called @dfn{loop commands} (@ref{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}
(@ref{Special Macro Arguments, , Special Macro Arguments}).

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

In general@comma{} a loop ends when any looping condition required by a loop command
evaluates to @code{nil}.  If no conditions are needed@comma{} then the loop runs infinitely
until an early-exit command is reached (@ref{Early Exit, , Exiting the Loop Early}).

The default return value of the loop is @code{nil}.  Other return values must be
stated explicitly@comma{} as in one of the early-exit commands or as part of the
@samp{finally-return} macro argument@comma{} or come from accumulating loop commands using
an implied accumulation variable (@ref{Accumulation, , Accumulation Commands}).

The @code{loopy} macro is configurable.  One can add custom commands (@ref{Custom Commands, , Custom
Commands})@comma{} add custom command aliases (@ref{Custom Aliases, , Custom Aliases})@comma{} and specify macro
options for a particular loop (@ref{Using Flags, , Using Flags}).  Each of these features is
explained in detail later in this document.

@node Special Macro Arguments
@chapter Special Macro Arguments

@cindex special macro argument
There are only a few special macro arguments.  If a macro argument does not
match one of these special few@comma{} @code{loopy} will attempt to interpret it as a loop
command@comma{} and will signal an error if that fails.

These special macro arguments are always processed before loop commands@comma{}
regardless of the order of the arguments passed to @code{loopy}.

For clarity@comma{} some special macro arguments have multiple names. For example@comma{} the
special macro argument @samp{with} can also be written as @samp{init}@comma{} because the
argument @samp{with} is used to initialized variables.

@pindex named
@table @asis
@item @samp{named} or just a symbol
Name the loop.  This also names the @code{cl-block}
which contains the loop.  This can be of the form @samp{(named NAME)} or just
@samp{NAME}.

@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 lisp
@end table

@pindex with
@pindex let*
@pindex init
@table @asis
@item @samp{with}@comma{} @samp{let*}@comma{} @samp{init}
Declare variables before the loop@comma{} in order.  This
can also be used to initialize variables referenced by loop commands.  @samp{with}
can use destructuring (@ref{Basic Destructuring}).

@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'@comma{} `b'@comma{} and each `i' into a list.
@end lisp
@end table

@pindex without
@pindex no-with
@pindex no-init
@table @asis
@item @samp{without}@comma{} @samp{no-with}@comma{} @samp{no-init}
Variables that @code{loopy} should not try to
initialize.  @code{loopy} tries to initialize all of the variables that it uses
in a @code{let}-like form@comma{} but that isn’t always desired.

@lisp
;; Without `without'@comma{} `loopy' would try to initialize `a' to nil@comma{} 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 lisp
@end table

@pindex before
@pindex before-do
@pindex initially-do
@pindex initially
@table @asis
@item @samp{before-do}@comma{} @samp{before}@comma{} @samp{initially-do}@comma{} @samp{initially}
Run Lisp expressions
before the loop starts@comma{} after variables are initialized.

@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 lisp
@end table

@pindex after-do
@pindex after
@pindex else-do
@pindex else
@table @asis
@item @samp{after-do}@comma{} @samp{after}@comma{} @samp{else-do}@comma{} @samp{else}
Run Lisp expressions after the loop
successfully completes.  This is similar to Python’s @code{else} statement
following a @code{for} or @code{while} loop.  Unlike @code{progn}@comma{} the return values of the
expressions do not affect the return value of the macro.

@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 lisp
@end table

@pindex finally-do
@pindex finally
@table @asis
@item @samp{finally-do}@comma{} @samp{finally}
Run Lisp expressions after the loop exits@comma{} always.
Unlike @code{progn}@comma{} the return values of the expressions do not affect the
return value of the macro.

@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 lisp

When used with @samp{finally-return}@comma{} code in @samp{finally-do} is run before code in
@samp{finally-return}.

@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 lisp

@samp{finally-do} has access to the variables created by the loop after the loop
finishes@comma{} including accumulation variables that you might wish to use as a
return value (@ref{Accumulation}).  For consistency with explicit
accumulation variables (@ref{Optimizing Accumulations})@comma{} so long as the loop completes
successfully@comma{} @samp{finally-do} can modify @code{loopy-result} even when it is used
as an implied accumulation variable.

@lisp
;; => (0 1 2 3)
(loopy (list i '(1 2 3))
       (collect i)
       (finally-do (push 0 loopy-result)))
@end lisp
@end table

@pindex finally-return
@table @asis
@item @samp{finally-return}
Return a value@comma{} regardless of how the loop completes.
These arguments override any explicit return values given in commands like
@samp{return} and @samp{return-from}@comma{} 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.

@lisp
;; => 999
(loopy (return 1)
       (finally-return 999))

;; => (1 2)
(loopy (leave) ; Leave to avoid infinite loop.
       (finally-return 1 2))
@end lisp

When used with @samp{finally-do}@comma{} code in @samp{finally-return} is run after code in
@samp{finally-do}.

@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 lisp
@end table


@pindex finally-protect
@pindex finally-protected
@table @asis
@item @samp{finally-protect}@comma{} @samp{finally-protected}
Wrap the loop in @code{unwind-protect}
(not to be confused with @code{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@comma{} and
is only meant to help avoid lingering effects that might arise from
unplanned stops of the loop's execution.

@lisp
;; Prints out the following@comma{} 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 lisp
@end table

@pindex flag
@pindex flags
@table @asis
@item @samp{flag}@comma{} @samp{flags}
Options that change the behavior of @code{loopy} (@ref{Using Flags}).
For example@comma{} one can opt to use a different destructuring system@comma{} such as
what is provided by the Dash library.  See that linked section for more
information.

@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 lisp
@end table

@pindex accum-opt
@pindex opt-accum
@table @asis
@item @samp{accum-opt}@comma{} @samp{opt-accum}
Accumulation variables whose use should be
optimized (@ref{Optimizing Accumulations}).  Implicit accumulation variables are always
optimized@comma{} but explicit variables are unoptimized by default
(@ref{Accumulation}).  This special macro argument allows optimizing named
variables (with restrictions)@comma{} which is useful when using more than one
accumulation variable.  This is especially important for destructuring
accumulations.

@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 lisp
@end table


@pindex wrap
@table @asis
@item @samp{wrap}
A list of forms in which to wrap the loop itself (that is@comma{} not
@samp{before-do}@comma{} @samp{after-do}@comma{} or anything else).  Each form can be either a list
or a symbol.  If a list@comma{} the loop is inserted into the end of the list.  If
a symbol@comma{} 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 @code{thread-last}@comma{} except that forms listed first are
applied last@comma{} 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 @samp{before-do}
is run.

@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 lisp
@end table


As stated above@comma{} all other expressions will be considered loop commands
(@ref{Loop Commands, , Loop Commands}).

@quotation Note
For convenience@comma{} the @code{while}-loop that @code{loopy} creates is wrapped by a
@code{cl-block}.  Naming the loop names this block@comma{} which is created @emph{after}
initializing variables.

The two special macro arguments @samp{before-do} and @samp{after-do} (and their aliases)
also occur within this @code{cl-block}@comma{} before and after the loop@comma{} respectively.
This has 2 consequences:

@enumerate
@item
Using @code{cl-return} in @samp{before-do} will prevent the both loop and @samp{after-do}
code from running.

@item
Using @code{cl-return} or an early exit command (@ref{Early Exit, , Early Exit}) in the loop will
prevent the @samp{after-do} code from running.  For this reason@comma{} @samp{after-do} is
run if and only if the loop completes successfully@comma{} hence the alias
@samp{else-do} and the similarity to Python's @code{else} statement when used with
loops.
@end enumerate

These three sections (@samp{before-do}@comma{} @samp{after-do}@comma{} and the @code{while}-loop itself)
are the only structures that occur within the @code{cl-block}.  Using @code{cl-return}
in @samp{before-do}@comma{} for example@comma{} will not stop code in @samp{finally-do} from running
or values listed in @samp{finally-return} from being returned.

@end quotation

@node Loop Commands
@chapter Loop Commands

@cindex loop command
If a macro argument does not match one of the previously listed special macro
arguments (@ref{Special Macro Arguments, , Special Macro Arguments})@comma{} @code{loopy} will attempt to treat it as a loop
command.  Loop commands are only valid as a top-level argument to the macro@comma{} or
inside another loop command.

Therefore@comma{} these macro calls are valid:

@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 lisp

and this is not:

@lisp
(loopy (with (list i '(1 2 3)))
       (finally-return (collect coll i)))
@end lisp

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@comma{} as @samp{i}
is assigned a value from the list after collecting @samp{i} into @samp{coll}.

@float Listing
@lisp
;; => (nil 1 2)
(loopy (collect coll i)
       (list i '(1 2 3))
       (finally-return coll))
@end lisp
@caption{An example of how loop commands are evaluated in order.}
@end float

For convenience and understanding@comma{} the same command might have multiple names@comma{}
called @dfn{aliases}.  For example@comma{} the @samp{array} command has the alias
@samp{string}@comma{} because the @samp{array} command can be used to iterate through the
elements of an array or string@footnote{Strings being a kind of array.  See @ref{Sequences Arrays Vectors,,,elisp,}
for more.}.  Implementation-wise@comma{} 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 @code{loopy-parsers}.  You can define custom aliases by modifying this
variable (@ref{Custom Aliases, , Custom Aliases}).

Similar to other libraries@comma{} many commands have an alias of the
present-participle form (the ``-ing'' form).  A few examples are seen in the table
below.

@multitable {aaaaaaaaa} {aaaaaaaaaaaa}
@headitem Command
@tab ``-ing'' Alias
@item @samp{set}
@tab @samp{setting}
@item @samp{list}
@tab @samp{listing}
@item @samp{collect}
@tab @samp{collecting}
@item @samp{numbers}
@tab @samp{numbering}
@end multitable

Some commands take optional keyword arguments.  For example@comma{} the command @samp{list}
can take a function argument following the keyword @samp{:by}@comma{} which affects how that
command iterates through the elements in the list.

For simplicity@comma{} the commands are described using the following notation:

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


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

@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 lisp

@cindex keyword evaluation
Unlike @code{cl-loop} in some cases@comma{} in Loopy@comma{} the values passed as keyword arguments
are evaluated only once.  For example@comma{} the command @samp{(list i some-list :by
(get-function))} evaluates @code{(get-function)} only once.  It does not evaluate it
repeatedly for each step of the loop.

@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 lisp

@menu
* Basic Destructuring::          How to destructure variables and values in loop commands.
* Generic Evaluation::           Setting variables, evaluating expressions, etc.
* Iteration::                    Iterating through sequences, etc.
* Accumulation::                 Accumulating values into new sequences, aggregating values, etc.
* Checking Conditions::          Testing whether a condition holds true.
* Control Flow::                 When to run loop commands.
* Sub-Loops::                    Running a loop within a loop.
@end menu

@node Basic Destructuring
@section Basic Destructuring

@cindex variable destructuring
Similar to features like @code{seq-let}@comma{} @code{cl-destructuring-bind}@comma{} and @code{pcase-let}@comma{}
@code{loopy} is capable of destructuring values when assigning values to variables.
Destructuring in Loopy is similar to@comma{} but more featureful than@comma{} what is
provided in @samp{cl-lib}.

Some differences include:
@itemize
@item
Destructuring arrays

@item
Destructuring in accumulation commands (@ref{Accumulation})

@item
Destructuring in commands iterating through @code{setf}-able places in a sequence
(@ref{Sequence Reference Iteration})

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

@item
A @samp{&map} construct@comma{} similar to @samp{&key}@comma{} but using @code{map-elt} instead of
@code{plist-get} and which does not error when the map contains keys which aren't
matched (in other words@comma{} there is no need for an equivalent of
@samp{&allow-other-keys}).
@end itemize


This section describes the basic built-in destructuring used by most loop
commands@comma{} such as @samp{set} and @samp{list}.  Destructuring in accumulation commands
(@ref{Accumulation}) and sequence-reference commands
(@ref{Sequence Reference Iteration}) works slightly differently@comma{} and is described
more in those sections.

@cindex pcase pattern loopy
In addition to what can be done in loop commands@comma{} several features are available
for using Loopy's destructuring outside of @code{loopy} loops (@ref{Destructuring Macros})@comma{}
including the @code{pcase} pattern @samp{loopy}.

The last thing to note is that @code{loopy} loops can be made to use alternative
destructuring systems@comma{} such as @code{seq-let} or @code{pcase-let}.  This is done by using
the @samp{flag} special macro argument (@ref{Using Flags}).  If you are familiar with the
package @samp{dash} @footnote{@uref{https://github.com/magnars/dash.el}} and its Clojure-style destructuring@comma{} consider trying
the flag @samp{dash} provided by the package @samp{loopy-dash}.


Below are two examples of destructuring in @code{cl-loop} and @code{loopy}.

@float Listing
@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 lisp
@caption{Destructuring values in a list.}
@end float

@float Listing
@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 lisp
@caption{Destructuring values in assignment.}
@end float


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

@itemize
@item
To destructure lists@comma{} use a list@comma{} as in @samp{(a b c)}.

@item
To destructure arrays@comma{} use a vector@comma{} as in @samp{[a b c]}.

@item
To destructure sequences generically using @samp{seq.el} (mainly via @code{seq-elt} and
@code{seq-drop})@comma{} use a vector or a list whose first element is @samp{&seq}@comma{} as in
@samp{[&seq a b c]} and @samp{(&seq a b c)}.
@end itemize


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

@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 lisp

The content of this destructuring sequence is similar to @samp{cl-lib}@comma{} and is

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

@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 lisp


In more detail@comma{} the elements of the destructuring sequence can be:

@itemize
@item
A positional variable which will be bound to the corresponding element in the
sequence.  These variables can themselves be sequences@comma{} but must be of the
correct type.

@lisp
;; ((1 2 3) (4 5 6))
(loopy (list [i (j k)] '([1 (2 3)] [4 (5 6)]))
       (collect (list i j k)))
@end lisp
@end itemize

@cindex _
@itemize
@item
The symbol @samp{_} (an underscore) or a symbol beginning with an underscore: This
means to ignore the element at this location.  This can be more efficient.

@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@comma{}
;; 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 lisp
@end itemize

@cindex &whole
@itemize
@item
The symbol @samp{&whole}: If @samp{&whole} is the first element in the sequence (or the
second element if @samp{&seq} is the first)@comma{} 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 @code{lambda} list.

@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 lisp
@end itemize

@cindex &rest
@itemize
@item
The symbol @samp{&rest}: A variable named after @samp{&rest} contains the remaining
elements of the destructured value after any positional and optional values.
When destructuring lists@comma{} one can also use dotted notation@comma{} as in a CL
@code{lambda} list.  These variables can themselves be sequences to be further
destructured.

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

@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 lisp

This @samp{&rest} is the same as when used in @code{seq-let}.

@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 lisp
@end itemize

@cindex &optional
@itemize
@item
The symbol @samp{&optional}: A variable named after @samp{&optional} is bound if the
sequence is long enough to have a value at that position.  If the sequence is
not long enough@comma{} then the variable is bound to @code{nil} or@comma{} if specified@comma{} a
default value.  Additionally@comma{} one may bind a variable to record whether the
sequence was long enough to contain the optional value.

As in a CL @code{lambda} list@comma{} the variable has the one of the following forms:

@itemize
@item
@samp{(VAR DEFAULT SUPPLIED)} or @samp{[VAR DEFAULT SUPPLIED]}@comma{} in which @samp{VAR} itself
can be a sequence

@item
@samp{(VAR DEFAULT)} or @samp{[VAR DEFAULT]}@comma{} in which @samp{VAR} itself can be a sequence

@item
@samp{(VAR)} or @samp{[VAR]}@comma{} in which @samp{VAR} itself can be a sequence

@item
a symbol @samp{VAR}
@end itemize

@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 lisp

@samp{&optional} cannot be used after @samp{&rest}.

@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 lisp
@end itemize

@cindex &key
@cindex &keys
@itemize
@item
The symbol @samp{&key} or @samp{&keys}: Variables named after @samp{&key} are transformed
into keys whose values will be sought using @code{plist-get}@comma{} which returns @code{nil}
if the key isn't found in the list.

Only lists support this destructuring.

@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 lisp

Variables after @samp{&key} can be of the following forms:

@itemize
@item
@samp{((VAR KEY) DEFAULT SUPPLIED)}@comma{} @samp{[[VAR KEY] DEFAULT SUPPLIED]}@comma{} @samp{([VAR KEY]
    DEFAULT SUPPLIED)}@comma{} or @samp{[(VAR KEY) DEFAULT SUPPLIED]}@comma{} in which @samp{VAR} itself
can be a sequence

@item
@samp{((VAR KEY) DEFAULT)}@comma{} @samp{[[VAR KEY] DEFAULT]}@comma{} @samp{([VAR KEY] DEFAULT)}@comma{} or
@samp{[(VAR KEY) DEFAULT]}@comma{} in which @samp{VAR} itself can be a sequence

@item
@samp{((VAR KEY))}@comma{} @samp{[[VAR KEY]]}@comma{} @samp{([VAR KEY])}@comma{} or @samp{[(VAR KEY)]}@comma{} in which
@samp{VAR} itself can be a sequence

@item
@samp{(VAR DEFAULT SUPPLIED)} or @samp{[VAR DEFAULT SUPPLIED]}@comma{} in which @samp{VAR} is a
symbol

@item
@samp{(VAR DEFAULT)} or @samp{[VAR DEFAULT]}@comma{} in which @samp{VAR} is a symbol

@item
@samp{(VAR)} or @samp{[VAR]}@comma{} in which @samp{VAR} is a symbol

@item
a symbol @samp{VAR}
@end itemize

If a default value is provided@comma{} then keys are sought using @code{plist-member}.
That way@comma{} a value of @code{nil} for a key is not the same as a missing key.

@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 lisp

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

@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 lisp

Keys are sought in values after those bound to positional variables@comma{} which can
be the same values bound to the variable named by @samp{&rest} when both are
used.

@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@comma{} 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 lisp

@samp{&key} and @samp{&rest} can be used in any order@comma{} but @samp{&key} must come before
the dot in dotted lists.

@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 lisp

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

@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 lisp
@end itemize

@cindex &map
@itemize
@item
The symbol @samp{&map}: Variables after @samp{&map} are bound similarly to @code{map-let}
from the library @samp{map.el}.  @samp{&map} works similarly to @samp{&key}@comma{} but has a few
important differences:

@enumerate
@item
Maps are more generic than property lists (``plists'').  A ``map'' is a generic
structure which supports the function @code{map-elt}.  The built-in maps are
arrays@comma{} property lists (``plists'')@comma{} association lists (``alists'')@comma{} and hash
tables.  This generality means that it is slower than @samp{&key} for property
lists@comma{} though the difference should be small.

@item
@samp{&map} will not signal an error if there are unused keys inside the
destructured value; there is no @samp{&allow-other-keys} for @samp{map}.  In the same
vein@comma{} it cannot be made to signal an error if there are unused keys.
@end enumerate

Variables after @samp{&map} can be of the following forms:

@itemize
@item
@samp{(KEY VAR DEFAULT SUPPLIED)} or @samp{[KEY VAR DEFAULT SUPPLIED]}@comma{} in which @samp{VAR}
itself can be a sequence

@item
@samp{(KEY VAR DEFAULT)} or @samp{[KEY VAR DEFAULT]}@comma{} in which @samp{VAR} itself can be a
sequence

@item
@samp{(KEY VAR)} or @samp{[KEY VAR]}@comma{} in which @samp{VAR} itself can be a sequence

@item
@samp{(VAR)} or @samp{[VAR]}@comma{} in which @samp{VAR} must be a symbol

@item
a symbol @samp{VAR}
@end itemize

When @samp{KEY} is not given@comma{} then the key is the symbol @samp{VAR}@comma{} as in @code{(quote
  VAR)}.  Unlike with @samp{&key}@comma{} it is not prepended with a colon.

@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@comma{} 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 lisp

When @samp{&map} and @samp{&key} are used together@comma{} they search through the same
values.  The use of both is normally redundant.

@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 lisp

@item
The symbol @samp{&aux}: Variables named after @samp{&aux} are bound to the given values.
Like in CL Lib@comma{} @samp{&aux} must come last in the sequence.

@lisp
;; => (7 7 7)
(loopy (cycle 3)
       (collect (&aux [coll 7]) 'ignored)
       (finally-return coll))
@end lisp

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

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

@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 lisp
@end itemize

@node Generic Evaluation
@section Generic Evaluation

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
@table @asis
@item @samp{(do EXPRS)}
Evaluate multiple Lisp expressions@comma{} like a @samp{progn}.

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

To use loop commands in arbitrary code@comma{} use the macro @code{loopy-iter} instead
(@ref{The @code{loopy-iter} Macro}).

@lisp
(loopy (list i '(1 2 3))
       (do (message "%d" i)))
@end lisp
@end table

@tindex command-do
@table @asis
@item @samp{(command-do [CMDS])}
Evaluate multiple loop commands@comma{} as if in a
@samp{progn}.  This is similar to @samp{do}@comma{} but runs commands instead of normal Lisp
expressions.  Currently@comma{} this command is only useful when used within the
@samp{if} command.

@lisp
;; Report whether an even number is found@comma{} or return the sum of
;; the list's elements.  To be clear@comma{} 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 lisp
@end table

@tindex set
@tindex setting
@table @asis
@item @samp{(set VAR EXPR [EXPRS])}
Bind @samp{VAR} to each @samp{EXPR}@comma{} in order. In the n-th
cycle of the loop@comma{} bind @samp{VAR} to the n-th @samp{EXPR}.  Once the last
@samp{EXPR} is reached@comma{} it is used repeatedly for the rest of the loop.

This command also has the aliases @samp{setting}.

Unlike the Emacs Lisp function @code{set}@comma{} the variable name should not be quoted.
Unlike the Emacs Lisp special form @code{setq}@comma{} the command @samp{set} only sets one
variable@comma{} and this variable is by default @code{let}-bound around the loop.  To
stop @samp{VAR} from being @code{let}-bound around the loop@comma{} use the special macro
argument @samp{without} (@ref{Special Macro Arguments}).

@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 lisp
@end table

@tindex set-prev
@tindex setting-prev
@tindex prev-set
@table @asis
@item @samp{(set-prev VAR VAL &key back)}
Bind @samp{VAR} to a value @samp{VAL} from a previous
cycle in the loop.  With @samp{BACK} (default: 1)@comma{} use the value from that many
cycles previous.  If not enough cycles have passed yet@comma{} then the value of
@samp{VAR} is not modified.  This command @emph{does not} work like a queue for
recording @samp{VAL}; it always uses the value from the @samp{BACK}-th previous cycle@comma{}
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 @samp{setting-prev} and@comma{} for typo tolerance@comma{}
@samp{prev-set}.

@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'@comma{}
;;       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 lisp
@end table

@node Iteration
@section Iteration

@dfn{Iteration commands} bind local variables and determine when the loop
ends.  If no command sets an ending condition@comma{} then the loop runs forever.
Infinite loops can be exited by using early-exit commands
(@ref{Early Exit}) or boolean commands (@ref{Checking Conditions}).

Iteration commands must occur in the top level of the macro or in the top level
of a sub-loop command (@ref{Sub-Loops}).  Using them elsewhere and trying to do
something like the below example will signal an error.

@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 lisp

In @code{loopy}@comma{} iteration commands are named after what they iterate through.  For
example@comma{} the @samp{array} and @samp{list} commands iterate through the elements of arrays
and lists@comma{} respectively.

Because some iteration commands use their variable to manage state@comma{} it is an
error to use the same iteration variable for multiple iteration commands.

@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 lisp

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

@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 lisp

Be aware that @code{cl-loop} does not consistently initialize its iteration variables
to nil.  For some of @code{cl-loop}'s iteration (@samp{for}) statements@comma{} 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@comma{} as seen in the
below example@comma{} but that can result in unnecessary indirection for some use
cases@comma{} which has a minor speed cost.

@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 lisp

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

@lisp
;; => 2@comma{} not 3
(let ((i 0))
  (loopy (do (setq i (1+ i)))
         (list elem '(0 1)))
  i)
@end lisp

If you do wish to conditionally leave the loop during an iteration@comma{} consider
using the @samp{leave} and @samp{leave-from} commands (@ref{Early Exit}).

@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 lisp

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

@menu
* Generic Iteration::            Looping a certain number of times.
* Numeric Iteration::            Iterating through numbers.
* Sequence Iteration::           Iterating through sequences.
* Sequence Index Iteration::     Iterating through indices without accessing values.
* Sequence Reference Iteration:: Iterating through places/fields in sequences.
@end menu

@node Generic Iteration
@subsection Generic Iteration

@tindex cycle
@tindex cycling
@tindex repeat
@tindex repeating
@table @asis
@item @samp{(cycle|repeat [VAR] EXPR)}
Run the loop for @samp{EXPR} iterations.

If given@comma{} then during the loop@comma{} @samp{VAR} is set to the number of iteration steps
that have been run (0 for the first iteration step).

If @samp{EXPR} is 0@comma{} then the loop isn't run.

@samp{(cycle VAR EXPR)} works the same as @samp{(numbers VAR :from 0 :below EXPR)}
(@ref{Numeric Iteration}).

This command also has the aliases @samp{cycling} and @samp{repeating}.

@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 lisp
@end table

@tindex iter
@tindex iterating
@table @asis
@item @samp{(iter [VAR] EXPR &key close yield-result)}
Iterate through the values
returned by an Emacs Lisp iterator (@ref{Generators,,,elisp,}).  @samp{EXPR} is an
iterator object produced by a calling a generator function.  If given@comma{} @samp{VAR}
holds the value yielded by the iterator. The loop ends when the iterator
finishes.

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

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

This command also has the name @samp{iterating}.

@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 lisp

@quotation Warning
Be aware that values are yielded from the iterator before running the loop
body.  When the iterator can no longer yield values@comma{} it is finished.

Because values are yielded before the next iteration step of the loop@comma{} 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
@samp{set} with the function @code{iter-next} directly.

@end quotation

@lisp
;; => 5@comma{} 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 lisp
@end table

@node Numeric Iteration
@subsection Numeric Iteration

For iterating through numbers@comma{} there is the @samp{numbers} command and its
more limited variants @samp{numbers-up} and @samp{numbers-down}.

@tindex num
@tindex nums
@tindex number
@tindex numbering
@tindex numbers
@table @asis
@item @samp{(numbers|nums VAR &key KEYS)}
Iterate through numbers.  @samp{KEYS} is one or
several of @samp{from}@comma{} @samp{upfrom}@comma{} @samp{downfrom}@comma{} @samp{to}@comma{} @samp{upto}@comma{} @samp{downto}@comma{} @samp{above}@comma{}
@samp{below}@comma{} @samp{by}@comma{} and @samp{test}.

This command also has the aliases @samp{num}@comma{} @samp{number}@comma{} and @samp{numbering}.

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

In its most basic form@comma{} @samp{numbers} iterates from a starting value to an
inclusive ending value using the @samp{:from} and @samp{:to} keywords@comma{} respectively.

@lisp
;; => (1 2 3 4 5)
(loopy (numbers i :from 1 :to 5)
       (collect i))
@end lisp

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

@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@comma{} 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 lisp

If the ending value is not given@comma{} then the value is incremented by 1 without
end.

@lisp
;; => (7 8 9 10 11 12 13 14 15 16)
(loopy (cycle 10)
       (numbers i :from 7)
       (collect i))
@end lisp

To specify the step size@comma{} one can use the keyword @samp{:by}.  Except when @samp{:test}
is given@comma{} the value for @samp{:by} must be positive.  Other keyword arguments
(that is@comma{} @samp{:upfrom}@comma{} @samp{:downfrom}@comma{} @samp{:upto}@comma{} @samp{:downto}@comma{} @samp{:above}@comma{} and @samp{:below})
control whether the variable is incremented or decremented.

@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 lisp

By default@comma{} the variable's value starts at 0 and increases by 1.  To specify
whether the value should be increasing or decreasing when using the @samp{:by}
keyword@comma{} one can use the keywords @samp{:downfrom}@comma{} @samp{:downto}@comma{} @samp{:upfrom}@comma{} @samp{:upto}@comma{}
@samp{:above}@comma{} and @samp{:below}.  The keywords @samp{:from} and @samp{:to} don't by themselves
specify a direction@comma{} 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.

@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 lisp

To specify an @emph{exclusive} ending value@comma{} use the keywords @samp{:below} for
increasing values and @samp{:above} for decreasing values.

@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 lisp

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

@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'@comma{} 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 lisp
@end table

If you prefer using positional arguments to keyword arguments@comma{} you can use the
commands @samp{numbers-up} and @samp{numbers-down} to specify directions.  These commands
are simple wrappers of the above @samp{numbers} command.

@tindex nums-down
@tindex numbers-down
@tindex numbering-down
@table @asis
@item @samp{(numbers-down|nums-down VAR START [END] &key by)}
Equivalent to @samp{(numbers
  VAR :from START [:downto END] &key by)}.  This command exists only for convenience.

This command also has the aliases @samp{numsdown} and @samp{numbering-down}.

@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 lisp
@end table

@tindex nums-up
@tindex numbers-up
@tindex numbering-up
@table @asis
@item @samp{(numbers-up|nums-up VAR START [END] &key by)}
Equivalent to @samp{(numbers VAR
  :from START [END] &key by)}.  This command exists only for convenience.

This command also has the aliases @samp{numsup} and @samp{numbering-up}.

@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 lisp
@end table

@node Sequence Iteration
@subsection Sequence Iteration

These commands provide various ways to iterate through sequences
(@ref{Sequences Arrays Vectors,,,elisp,}).

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

@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 lisp

The @samp{array} and @samp{sequence} commands can use the same keywords as the @samp{numbers}
command (@ref{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@comma{} they also have an @samp{index} keyword@comma{} which names the variable used to
store the accessed index during the loop.

@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 lisp

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

@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 lisp


@tindex array
@tindex string
@tindex arraying
@tindex stringing
@table @asis
@item @samp{(array|string VAR EXPR [EXPRS] &key KEYS)}
Loop through the
elements of the array @samp{EXPR}.  In Emacs Lisp@comma{} strings are arrays whose
elements are characters.

This command also has the aliases @samp{arraying} and @samp{stringing}.

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

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

@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 lisp
@end table

@tindex cons
@tindex conses
@tindex consing
@table @asis
@item @samp{(cons|conses VAR EXPR &key by)}
Loop through the cons cells of @samp{EXPR}.
Optionally@comma{} find the cons cells via the function @samp{by} instead of @samp{cdr}.

This command also has the alias @samp{consing}.

@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 lisp
@end table

@tindex list
@tindex listing
@tindex each
@table @asis
@item @samp{(list|each VAR EXPR [EXPRS] &key by)}
Loop through each element of the
list @samp{EXPR}.  Optionally@comma{} move through the list using @samp{by} instead of @samp{cdr}.

This command also has the alias @samp{listing}.

If multiple lists are given@comma{} distribute the elements of the lists into one new
list.  In such cases@comma{} @samp{by} applies to the new list@comma{} not the arguments of the
command.

@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 lisp
@end table

@tindex map
@tindex map-pairs
@tindex mapping
@tindex mapping-pairs
@table @asis
@item @samp{(map|map-pairs VAR EXPR &key unique)}
Iterate through the dotted key-value
pairs of map @samp{EXPR}@comma{} using the function @code{map-pairs} from the @samp{map.el} library.
This library generalizes working with association lists (``alists'')@comma{} property
lists (``plists'')@comma{} hash tables@comma{} and vectors.

This command also has the aliases @samp{mapping} and @samp{mapping-pairs}.

In each dotted pair assigned to @samp{VAR}@comma{} the @code{car} is the key and the @code{cdr} is
the value.

By default@comma{} only the unique keys are used.  To disable this deduplication@comma{}
pass @code{nil} to the @samp{unique} keyword argument.

In general@comma{} as a map in not necessarily a sequence@comma{} 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 @code{map-pairs}.  In other
words@comma{} the map @samp{EXPR} is not processed progressively@comma{} but all at once.
Therefore@comma{} this command can have a noticeable start-up cost when working with
very large maps.

@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@comma{} 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 lisp

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

Again@comma{} this can be disabled by setting @samp{unique} to nil.

@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@comma{} `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@comma{} `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 lisp
@end table

@tindex sequence
@tindex sequencing
@table @asis
@item @samp{(sequence VAR EXPR [EXPRS] &key KEYS)}
Loop through the sequence
@samp{EXPR}@comma{} binding @samp{VAR} to the elements of the sequence (a list or an array).
Because it is more generic@comma{} @samp{sequence} is somewhat less efficient than the
@samp{list} and @samp{array} commands.

@quotation Note
For more on sequences@comma{} see @ref{Sequences Arrays Vectors,,,elisp,}.  This command
works with the basic sequences understood by the Emacs Lisp functions @code{length}
and @code{elt}.  It does not work with the generic sequences understood by the
library @samp{seq.el}.  For those@comma{} use the @samp{seq} command.

@end quotation

This command also has the alias @samp{sequencing}.

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

@quotation Warning
Array elements can be accessed in constant time@comma{} but not list elements.  For
lists@comma{} the @samp{sequence} command is fastest when moving forwards through the
list.  In that case@comma{} the command does not have to search from the beginning of
the list each time to find the next element.  The @samp{sequence} command can be
noticeably slower for lists when working backwards or when the @samp{test}
parameter (for which direction cannot be assumed) is provided.

@end quotation

If multiple sequences are given@comma{} then these keyword arguments apply to the
resulting sequence of distributed elements.

@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 lisp
@end table

@tindex seq
@tindex seqing
@table @asis
@item @samp{(seq VAR EXPR [EXPRS] &key KEYS)}
For a generic sequence which implements
the features of the library @samp{seq.el}@comma{} loop through the generic sequence
@samp{EXPR}@comma{} binding @samp{VAR} to the elements of the sequence.  Because it is more
generic@comma{} @samp{seq} can be slower than the @samp{sequence} command@comma{} which in turn is
somewhat less efficient than the @samp{list} and @samp{array} commands.

If multiple generic sequences are given@comma{} then these keyword arguments apply to
the resulting generic sequence of distributed elements.

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

This command also has the alias @samp{seqing}.

The @samp{seq} command naively loops through the generic sequence using @code{seq-elt}
and @code{seq-length}.  Because other packages might implement custom sequences
using lists@comma{} no special consideration is made for optimizing the @samp{seq} command
when given a list.

Because the @samp{seq} command currently uses the function @code{seq-length} to detect
when to leave the loop@comma{} it does not work with infinite sequences.  For
infinite sequences@comma{} consider using the @samp{stream} command.

@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 lisp
@end table

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

This command also has the alias @samp{streaming}.

@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 lisp
@end table

@tindex substream
@tindex substreaming
@table @asis
@item @samp{(substream VAR EXPR &key by length)}
Iterate through the sub-streams of
stream @samp{EXPR}@comma{} similar to the command @samp{cons}.  If @samp{by} is non-nil (default:
1)@comma{} then move to the next n-th substream during each iteration.  If @samp{length}
is given@comma{} then the substream bound to @samp{VAR} is only the specified length.

This command operates on the @samp{stream} type defined by the library @samp{stream}
@uref{https://elpa.gnu.org/packages/stream.html, from GNU ELPA}@comma{} which is not to be confused with the Emacs Lisp ``input streams''
and ``output streams'' used for reading and printing text (@ref{Read and Print,,,elisp,}).  The ``streams'' defined by the @samp{stream} library are like lazy sequences
and are compatible with features from the built-in @samp{seq} library@comma{} such as
@code{seq-elt} and @code{seq-do}.

Sub-streams can only be destructured using the @samp{&seq} feature of the default
destructuring method (@ref{Basic Destructuring, , Basic Destructuring})@comma{} or by using the @samp{seq} flag
(@ref{Using Flags, , Using Flags}).  Streams are neither lists nor arrays.

This command also has the alias @samp{substreaming}.

@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@comma{}
       ;; 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 lisp
@end table

@node Sequence Index Iteration
@subsection Sequence Index Iteration

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 @samp{array} and @samp{seq} commands@comma{} the @samp{seq-index} command can use the
same keywords as the @samp{numbers} command (@ref{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
@table @asis
@item @samp{(sequence-index VAR EXPR &key KEYS)}
Iterate through the indices of @samp{EXPR}.

There is only one implementation of this command; there are no
type-specific versions.  This command also has the following aliases:
@itemize
@item
@samp{array-index}@comma{} @samp{arraying-index}
@item
@samp{list-index}@comma{} @samp{listing-index}
@item
@samp{string-index}@comma{} @samp{stringing-index}
@item
@samp{sequencing-index}
@item
@samp{seq-index}@comma{} @samp{seqing-index}
@end itemize
@end table


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

@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 lisp

This command does not support destructuring.

@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 lisp

@node Sequence Reference Iteration
@subsection Sequence Reference Iteration

These commands all iterate through @code{setf}-able places as generalized
variables (@ref{Generalized Variables,,,elisp,}).  These generalized variables
are commonly called ``references''@comma{} ``fields''@comma{} or ``places''.  The below example
demonstrates using @code{(nth 1 my-list)} and @code{(aref my-array 1)} as @code{setf}-able
places.

@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 lisp

Like other commands@comma{} ``field'' or ``reference'' commands can also use
destructuring@comma{} in which case the fields/places of the sequence are
destructured into ``sub-fields''@comma{} like the @code{cdr} of the second array element
in the example above.

@quotation Caution
Be aware that using @code{setf} on an array sub-sequence named by @samp{&rest}
will only overwrite values@comma{} not truncate or grow the array. 

@end quotation

@quotation Warning
Unfortunately@comma{} not all kinds of recursive destructuring work on references.

Currently:
@itemize
@item
@samp{&optional} variables are not supported
@item
@samp{SUPPLIED} variables are not supported for @samp{&key} and @samp{&map}.
@item
Non-nil default values for @samp{&optional}@comma{} @samp{&key}@comma{} and @samp{&map} are not supported.
@end itemize

@end quotation

As with the @samp{array} and @samp{seq} commands@comma{} the @samp{array-ref} and @samp{seq-ref}
commands can use the same keywords as the @samp{numbers} command
(@ref{Numeric Iteration}) for working with the index and choosing a range of the
sequence elements through which to iterate.  In addition to those keywords@comma{}
they also have an @samp{index} keyword@comma{} 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
@table @asis
@item @samp{(array-ref|string-ref VAR EXPR &key KEYS)}
Loop through the elements of
the array @samp{EXPR}@comma{} binding @samp{VAR} as a @code{setf}-able place.

This command also has the aliases @samp{arraying-ref} and @samp{stringing-ref}.

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

@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 lisp
@end table

@tindex list-ref
@tindex listing-ref
@table @asis
@item @samp{(list-ref VAR EXPR &key by)}
Loop through the elements of the list @samp{EXPR}@comma{}
binding @samp{VAR} as a @code{setf}-able place.  Optionally@comma{} update the list via
function @samp{by} instead of @code{cdr}.

This command also has the aliases @samp{listing-ref}.

@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 lisp
@end table

@tindex map-ref
@tindex mapping-ref
@table @asis
@item @samp{(map-ref VAR EXPR &key key unique)}
Loop through the values of map @samp{EXPR}@comma{}
binding @samp{VAR} as a @code{setf}-able place.  Like the command @samp{map}@comma{} this command
uses the @samp{map.el} library.

This command also has the alias @samp{mapping-ref}.

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

Like in the command @samp{map}@comma{} the keys of the map are generated via the function
@code{map-keys} before the loop is run@comma{} which can be expensive for large maps.

Similar to @samp{map}@comma{} any duplicate keys are ignored by default.  This can be
disabled by setting the @samp{unique} keyword argument to nil@comma{} 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.

@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 lisp
@end table

@tindex sequence-ref
@tindex sequencing-ref
@table @asis
@item @samp{(sequence-ref VAR EXPR &key KEYS)}
Loop through the elements of the
sequence @samp{EXPR} (an array or list)@comma{} binding @samp{VAR} as a @code{setf}-able place.

This command also has the aliases @samp{sequencing-ref}.

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

@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 lisp
@end table

@tindex seq-ref
@tindex seqing-ref
@table @asis
@item @samp{(seq-ref VAR EXPR &key KEYS)}
Loop through the elements of the generic
sequence @samp{EXPR}@comma{} via the features of the library @samp{seq.el}@comma{} binding @samp{VAR} as a
@code{setf}-able place.

@quotation Note
Not all generic sequences are mutable@comma{} so not all generic sequences work as
a @code{setf}-able place.

@end quotation

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

This command also has the alias @samp{seqing-ref}.

The @samp{seq-ref} command naively loops through the generic sequence using
@code{seq-elt} and @code{seq-length}.  Because other packages might implement custom
sequences using lists@comma{} no special consideration is made for optimizing the
@samp{seq-ref} command when given a list.

Because the @samp{seq-ref} command currently uses the function @code{seq-length} to
detect when to leave the loop@comma{} it does not work with infinite sequences.

@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 lisp
@end table

@node Accumulation
@section Accumulation

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

Unlike iteration commands@comma{} you can refer to the same accumulation variable in
multiple accumulation commands if needed.

@lisp
;; => (1 6 2 7 3 8)
(loopy (list i '(1 2 3))
       (collect coll i)
       (collect coll (+ i 5))
       (finally-return coll))
@end lisp

@quotation Note
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 quotation

@cindex accumulation destructuring
Similar to iteration commands@comma{} accumulation commands can also use destructuring.
In accumulation commands@comma{} the values resulting from destructuring are
accumulated@comma{} instead of the destructured value.

@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 lisp

@cindex implied accumulation results
Like in @code{cl-loop}@comma{} you do not need to supply a variable name to accumulation
commands.  If no accumulation variable is given and the @samp{finally-do} special
macro argument is not used@comma{} 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 @samp{return} and
@samp{return-from} loop commands or the @samp{finally-return} macro argument.

@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 lisp

@vindex loopy-result
Unlike @code{cl-loop}@comma{} Loopy uses a default accumulation variable@comma{} which is named
@code{loopy-result}.  When used this way@comma{} @code{loopy-result} is described as an
@dfn{implied accumulation variable} or an @dfn{implicit accumulation variable}.  This variable can be used in the @samp{after-do}@comma{} @samp{finally-do}@comma{} and
@samp{finally-return} special macro arguments.

@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 lisp

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

@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 lisp

In general@comma{} you should not attempt to modify or use the value of optimized
accumulation variables during the loop@comma{} including @code{loopy-result} when it is used
as the implied accumulation variable for a command@comma{} because they are not
guaranteed to have a correct value when efficiently building sequences
(@ref{Optimizing Accumulations}).  For some commands@comma{} such as those listed in @ref{Numeric Accumulation}
and @ref{Generic Accumulation}@comma{} this does not matter.

@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 lisp

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

@lisp
;; See that the `leave' command does not prevent finalizing
;; optimized accumulation variables@comma{} 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 lisp

Be aware that explicitly named accumulation variables do not affect the implied
return value of a loop.  Such values must be returned explicitly@comma{} 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@comma{} such as the possibly
intermediate variables that can be generated by @code{pcase} patterns.  This may
change in the future.

@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 lisp

Therefore@comma{} when mixing implicit and explicit accumulation variables@comma{} you must
always use the @samp{finally-return} special macro argument to return all of the
accumulation results.

@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 lisp

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

@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 lisp

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

@quotation Warning
Currently@comma{} a warning is raised when the default initial values of accumulation
commands conflict.  In the future@comma{} this will be an error.  To resolve this
conflict@comma{} use the @samp{with} special macro argument@comma{} as noted above.

@end quotation

@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 lisp

@menu
* Common Properties and Arguments of Accumulation Commands::
* Generic Accumulation::         Accumulating function output.
* Numeric Accumulation::         Using and returning numbers.
* Sequence Accumulation::        Using and returning sequences.
* Other Accumulation Commands::
* Optimizing Accumulations::     Producing efficient accumulations.
@end menu

@node Common Properties and Arguments of Accumulation Commands
@subsection Common Properties and Arguments of Accumulation Commands

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

@cindex accumulation keyword arguments
Some accumulation commands have optional keyword parameters@comma{} which are listed
in the command's definition.  To avoid repetition@comma{} the common parameters are
all described below.

@cindex accumulation keyword at
@table @asis
@item @samp{at}
Where to place a value.  One of @samp{end}@comma{} @samp{start}@comma{} or @samp{beginning}
(equivalent to @samp{start}).  If ungiven@comma{} defaults to @samp{end}.  These positions
need not be quoted.

@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 lisp
@end table

@cindex accumulation keyword into
@table @asis
@item @samp{into}
An alternative way to specify the variable into which to
accumulate values.  One would normally just give @samp{VAR} as the first
argument of the loop command@comma{} but if you wish@comma{} you can use this keyword
argument for a syntax more like @code{cl-loop}.

As all accumulation commands support this keyword@comma{} it is not listed in
any command definition.

@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 lisp
@end table

@cindex accumulation keyword test
@table @asis
@item @samp{test}
A function of two arguments@comma{} 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@comma{} the function should return a non-nil value.

@quotation Note
This argument is similar to the @samp{:test} argument used by @samp{cl-lib}@comma{} but is
closer to the optional @samp{testfn} argument used by @samp{seq} (for example@comma{} in
@code{seq-contains-p}).  There are two important differences:
@enumerate
@item
The default test function is @code{equal}@comma{} like in other Emacs Lisp libraries@comma{}
not @code{eql}.
@item
The first argument is the existing value or sequence and the second
argument is the tested value.  This is the @emph{opposite} of the order used by
@code{cl-member} and @code{memq}.
@end enumerate

@end quotation

@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 lisp
@end table

@cindex accumulation keyword key
@table @asis
@item @samp{key}
A one-argument function that transforms both the tested value and
the elements of the sequence passed to the function given by the @samp{test}
keyword.

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

@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 lisp
@end table


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

@node Generic Accumulation
@subsection Generic Accumulation

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

@itemize
@item
@samp{reduce} is like @code{cl-reduce}@comma{} calling a function that receives (1) the
accumulation variable and (2) the value to accumulate@comma{} in that order.
@item
@samp{accumulate} works by calling a function that receives (1) the value to
accumulate and (2) the accumulation variable@comma{} in that order.
@item
@samp{set-accum} is the most generic@comma{} and works like @samp{set} for only one value.
@end itemize


The commands are described in more detail below.

@tindex reduce
@tindex reducing
@table @asis
@item @samp{(reduce VAR EXPR FUNC)}
Reduce @samp{EXPR} into @samp{VAR} by @samp{FUNC}@comma{} like in
@code{cl-reduce} and @code{(funcall FUNC VAR EXPR)}.  @samp{FUNC} is called with @samp{VAR} as the
first argument and @samp{EXPR} as the second argument.  This is unlike
@samp{accumulate}@comma{} which gives @samp{VAR} and @samp{EXPR} to @samp{FUNC} in the opposite order
(that is@comma{} @samp{EXPR} first@comma{} then @samp{VAR}).

This command also has the alias @samp{reducing}.

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

@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 lisp

This command also has the alias @samp{callf}.  It is similar to using the
function @code{cl-callf}@comma{} except that the function argument is given last and
must be quoted.  This alias is intended to help users remember argument
order.

@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 lisp
@end table

@tindex accumulate
@tindex accumulating
@table @asis
@item @samp{(accumulate|accumulating VAR EXPR FUNC)}
Accumulate the result of applying
function @samp{FUNC} to @samp{EXPR} and @samp{VAR} like in @code{(funcall FUNC EXPR VAR)}.  @samp{EXPR}
and @samp{VAR} are used as the first and second arguments to @samp{FUNC}@comma{} respectively.

@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 lisp

This command also has the alias @samp{callf2}.  It is similar to using the function
@code{cl-callf2}@comma{} except that the function argument is given last and must be
quoted.  This alias is intended to help users remember argument order.

@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 lisp
@end table

@tindex set-accum
@tindex setting-accum
@table @asis
@item @samp{(set-accum VAR EXPR)}
Set the accumulation variable @samp{VAR} to the
value of @samp{EXPR}.

This command also has the alias @samp{setting-accum}.

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

@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 lisp
@end table

@node Numeric Accumulation
@subsection Numeric Accumulation

Numeric accumulation work on numbers@comma{} such as by repeatedly adding or
multiplying values together.

@tindex count
@tindex counting
@table @asis
@item @samp{(count VAR EXPR)}
Count the number of times that @samp{EXPR} evaluates to a
non-nil value.  @samp{VAR} starts at 0 and is incremented by 1 each time.

This command also has the alias @samp{counting}.

@lisp
;; => 3
(loopy (list i '(1 nil 3 nil 5))
       (count non-nil-count i)
       (finally-return non-nil-count))
@end lisp
@end table

@tindex max
@tindex maxing
@tindex maximize
@tindex maximizing
@table @asis
@item @samp{(maximize|max VAR EXPR)}
Repeatedly set @samp{VAR} to the greater of the values
@samp{VAR} and @samp{EXPR}.  @samp{VAR} starts at negative infinity (@code{-1.0e+INF})@comma{} so that
any other value should be greater that it.

This command also has the aliases @samp{maximizing} and @samp{maxing}.

@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 lisp
@end table

@tindex min
@tindex minimize
@tindex minnning
@tindex minimizing
@table @asis
@item @samp{(minimize|min VAR EXPR)}
Repeatedly set @samp{VAR} to the lesser of the values
@samp{VAR} and @samp{EXPR}.  @samp{VAR} starts at positive infinity (@code{1.0e+INF})@comma{} so that any
other value should be less than it.

This command also has the aliases @samp{minimizing} and @samp{minning}.

@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 lisp
@end table

@tindex multiply
@tindex multiplying
@table @asis
@item @samp{(multiply VAR EXPR)}
Repeatedly set @samp{VAR} to the product of
the values @samp{EXPR} and @samp{VAR}.  @samp{VAR} starts at 1.

This command also has the alias @samp{multiplying}.

@lisp
;; => 120
(loopy (list i '(1 2 3 4 5))
       (multiply 5-factorial i)
       (finally-return 5-factorial))
@end lisp
@end table

@tindex sum
@tindex summing
@table @asis
@item @samp{(sum VAR EXPR)}
Repeatedly set @samp{VAR} to the sum of the values of @samp{EXPR}
and @samp{VAR}.  @samp{VAR} starts at 0.

This command also has the alias @samp{summing}.

@lisp
;; => 10
(loopy (list i '(1 2 3 4))
       (sum my-sum i)
       (finally-return my-sum))
@end lisp
@end table

@node Sequence Accumulation
@subsection Sequence Accumulation

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

@tindex adjoin
@tindex adjoining
@table @asis
@item @samp{(adjoin VAR EXPR &key at test key)}
Repeatedly add @samp{EXPR} to @samp{VAR} if it
is not already present in the list.

This command also has the alias @samp{adjoining}.

Unlike @code{cl-adjoin} and like the other accumulation commands@comma{} this command
defaults to adjoining @samp{EXPR} to the end of @samp{VAR}@comma{} not the beginning.

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

@lisp
;; => ((1 . 1) (1 . 2) (2 . 3))
(loopy (list i '((1 . 1) (1 . 2) (1 . 2) (2 . 3)))
       (adjoin i))

;; No `:result-type'@comma{} 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 lisp
@end table

@tindex append
@tindex appending
@table @asis
@item @samp{(append VAR EXPR &key at)}
Repeatedly concatenate @samp{EXPR} to @samp{VAR}@comma{} as if
by the function @code{append}.

This command also has the alias @samp{appending}.

@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 lisp
@end table

@tindex collect
@tindex collecting
@table @asis
@item @samp{(collect VAR EXPR &key at)}
Collect the value of @samp{EXPR} into
the list @samp{VAR}.  By default@comma{} elements are added to the end of the list.

This command also has the alias @samp{collecting}.

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

@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 lisp
@end table

@tindex concat
@tindex concating
@table @asis
@item @samp{(concat VAR EXPR &key at)}
Repeatedly @code{concat} the value of @samp{EXPR} onto
@samp{VAR}@comma{} as a string.  For concatenating values into a vector@comma{} see the command
@samp{vconcat}.

This command also has the alias @samp{concating}.

@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 lisp
@end table

@tindex nconc
@tindex nconcing
@table @asis
@item @samp{(nconc VAR EXPR &key at)}
Repeatedly and @emph{destructively} concatenate the
value of @samp{EXPR} onto @samp{VAR} as if by using the function @code{nconc}.

This command also has the alias @samp{nconcing}.

@quotation Caution
@code{nconc} is a destructive operation that modifies @samp{VAR} directly
(@ref{Rearrangement,,,elisp,}).  This is important to keep in mind when working
with literal values@comma{} such as the list @samp{'(1 2 3)}@comma{} whose modification could
apply wherever that value is used (@ref{Self-Evaluating Forms,,,elisp,}).

@end quotation

@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 lisp
@end table

@tindex nunion
@tindex nunioning
@table @asis
@item @samp{(nunion VAR EXPR &key test at key)}
Repeatedly and @emph{destructively} insert
into @samp{VAR} the elements of @samp{EXPR} which are not already present in @samp{VAR}.

This command also has the alias @samp{nunioning}.

@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 lisp
@end table

@tindex prepend
@tindex prepending
@table @asis
@item @samp{(prepend VAR EXPR)}
Repeatedly concatenate @samp{EXPR} onto the front of @samp{VAR}@comma{}
as if by the function @code{append}.

This command also has the alias @samp{prepending}.

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

@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 lisp
@end table

@tindex push
@tindex pushing
@tindex push-into
@tindex pushing-into
@table @asis
@item @samp{(push-into|push VAR EXPR)}
Collect the value of @samp{EXPR} into a list@comma{} adding
values to the front of @samp{VAR} as if by using the function @code{push}.

This command also has the alias @samp{pushing} and @samp{pushing-into}.

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

@lisp
;; => (3 2 1)
(loopy (array i [1 2 3])
       (push my-list i)
       (finally-return my-list))
@end lisp
@end table

@tindex union
@tindex unioning
@table @asis
@item @samp{(union VAR EXPR &key test at key)}
Repeatedly insert into @samp{VAR} the
elements of the list @samp{EXPR} that are not already present in @samp{VAR}.

This command also has the alias @samp{unioning}.

@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 lisp
@end table

@tindex vconcat
@tindex vconcating
@table @asis
@item @samp{(vconcat VAR EXPR &key at)}
Repeatedly concatenate the value of @samp{EXPR}
onto @samp{VAR} via the function @code{vconcat}.  For concatenating values into a
string@comma{} see the command @samp{concat}.

This command also has the alias @samp{vconcating}.

@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 lisp
@end table

@node Other Accumulation Commands
@subsection Other Accumulation Commands

@tindex find
@tindex finding
@table @asis
@item @samp{(find VAR EXPR TEST &key ON-FAILURE)}
If the expression @samp{TEST} is non-nil@comma{}
then the loop stops and @samp{VAR} is set to the value of @samp{EXPR}.  If @samp{TEST} is
never non-nil@comma{} then @samp{VAR} is set to the value of @samp{ON-FAILURE}@comma{} if provided.

This command also has the alias @samp{finding}.

@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 lisp

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

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

Like in Common Lisp's Iterate package@comma{} if @samp{TEST} is a sharp-quoted symbol@comma{}
such as @code{#'cl-evenp}@comma{} then the test expression is treated as applying that
function to the value of @samp{EXPR}.

@lisp
;; => 2
(loopy (list i '(1 2 3 4 5 6))
       ;; (find i (cl-evenp i))
       (find i #'cl-evenp))
@end lisp

To be consistent with other commands@comma{} @samp{ON-FAILURE} is evaluated at the
start of the loop@comma{} even though that is not necessarily where it is used.

@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 lisp
@end table

@node Optimizing Accumulations
@subsection Optimizing Accumulations

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

@lisp
;; 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 lisp

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

@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 lisp

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

@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@comma{} 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 lisp

In such cases@comma{} @code{loopy} will naively optimize placing values at whichever side of
the sequences appears to be more used.  In the example below@comma{} note that even
though the commands to insert values at the front of the list are never actually
run@comma{} @code{loopy} will still optimize for frontal insertions.  Here@comma{} @code{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.

@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 lisp

The special macro argument @samp{accum-opt} can be used to better control these
optimizations (@ref{Special Macro Arguments}).  With it@comma{} 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 @samp{accum-opt} are either
symbols (such as @code{loopy-result}) or lists of a symbol and a position.  To be
clear@comma{} @code{loopy-result}@comma{} when used as an implied variable@comma{} is always at least
naively optimized in the manner described above.

In the example below@comma{} see that
@enumerate
@item
Accumulation into the named variable @code{coll} has been explicitly optimized for
using the end of the sequence@comma{} despite there being more commands that use the
beginning of the sequence.
@item
Because accumulation into @code{coll} has been optimized@comma{} the order of values in
@code{coll} need not be correct during the loop.
@end enumerate


@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 lisp

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

@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 lisp

@node Checking Conditions
@section Checking Conditions

@dfn{Boolean commands} are used to test whether a condition holds true
during the loop.  They work like a combination of accumulation commands
(@ref{Accumulation}) and early-exit commands (@ref{Early Exit})@comma{} in
that values are by default stored in @code{loopy-result} and that they can terminate
the loop without forcing a return value.

@quotation Note
Due to how the commands work@comma{} there are restrictions to how their target
variables can be used.  First@comma{} the @samp{always} and @samp{never} commands must use the
same variable to work together correctly.  Second@comma{} using the command @samp{thereis}
with the same variable as @samp{always} (and/or @samp{never}) is an error@comma{} as this would
create conflicting initial values for the implicit return value.

@end quotation


@tindex always
@table @asis
@item @samp{(always [VAR] EXPR &key into)}
Check the result of the condition @samp{EXPR}.
If the condition evaluates to @code{nil}@comma{} end the loop.  If the command was run@comma{}
return the value of the condition via @samp{VAR}.  Otherwise@comma{} if the command was
never run@comma{} return @code{t} via @samp{VAR}.

The steps are thus:
@enumerate
@item
The variable (by default@comma{} @code{loopy-result}) is initially bound to @code{t}@comma{} using
it as the implicit return value of the loop.
@item
When the condition is checked@comma{} the variable is bound to the value of
the condition.
@item
If the variable is @code{nil}@comma{} the loop is exited.
@item
If the loop completes successfully@comma{} then the variable is the final value
of the condition.  If the command is never run@comma{} then the variable will
remain @code{t}.
@end enumerate
@end table


@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@comma{} the implicit return value is `t' because an
;;       `always' command was used@comma{} 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 lisp

@tindex never
@table @asis
@item @samp{(never [VAR] EXPR &key into)}
Check the condition @samp{EXPR}.  If the
condition is ever non-@code{nil}@comma{} then the loop is exited and returns @code{nil} via
@samp{VAR}.  Otherwise the loop returns @code{t} via @samp{VAR}.

The steps are thus:
@enumerate
@item
The variable (by default@comma{} @code{loopy-result}) is initialized to @code{t} and used as
the loop's implicit return value.
@item
The value of the condition is checked.
@item
If the condition is non-@code{nil}@comma{} then the variable is set to @code{nil}
and the loop is exited.
@end enumerate
@end table


@quotation Note
Unlike the @samp{always} command@comma{} @samp{never} does not store any information in the
variable until it ends the loop.  Therefore@comma{} so long as the conditions of
@samp{never} are always @code{nil}@comma{} @samp{never} does not affect the loop's implicit return
value when using the @samp{always} command.

Be aware@comma{} though@comma{} that this behavior depends on @samp{always} and @samp{never} using
the same variable.

@end quotation

@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@comma{} not t
(loopy (cycle 2)
       (always 2)
       (never nil))
@end lisp

@tindex thereis
@table @asis
@item @samp{(thereis [VAR] EXPR &key into)}
Check the result of the condition @samp{EXPR}.
If the condition evaluates to a non-@code{nil} value@comma{} the loop returns that value
via @samp{VAR}.  Otherwise@comma{} the loop returns @code{nil} via @samp{VAR}.

The steps are thus:
@enumerate
@item
The variable (by default@comma{} @code{loopy-result}) is initialized to @code{nil} and used
as the implicit return value of the loop.
@item
The value of the condition is stored in the variable.
@item
If the value of the variable is non-@code{nil}@comma{} the loop exits.
@end enumerate
@end table


@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 lisp

@node Control Flow
@section Control Flow

This section describes:
@itemize
@item
how to conditionally execute loop commands
@item
how to skip loop cycles
@item
how to leave the loop early@comma{} with or without forcing a return value
@end itemize

@menu
* Conditional Execution::        Choosing if commands should run.
* Skipping Cycles::              Immediately beginning the next iteration.
* Early Exit::                   Leaving the loop early, with or without returning values.
@end menu

@node Conditional Execution
@subsection Conditional Execution

@dfn{Conditional execution commands} in @code{loopy} can take multiple
sub-commands@comma{} and work like their Lisp counterparts.  There is therefore no need
for an @samp{and} command as used in @code{cl-loop}.

@tindex cond
@table @asis
@item @samp{(cond [(EXPR CMDS) [...]])}
Run the commands @samp{CMDS} following the first
non-nil condition @samp{EXPR}.  This is the @code{loopy} version of the @code{cond} special
form from normal Emacs Lisp.

@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 lisp
@end table

@tindex if
@table @asis
@item @samp{(if EXPR CMDS)}
Run the first command if @samp{EXPR} is non-nil.  Otherwise@comma{}
run the remaining commands.  This is the @code{loopy} version of the @code{if} special
form from normal Emacs Lisp.

@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 lisp
@end table

@tindex when
@table @asis
@item @samp{(when EXPR CMDS)}
Run @samp{CMDS} only if @samp{EXPR} is non-nil.  This is the
@code{loopy} version of the @code{when} macro from normal Emacs Lisp.

@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 lisp
@end table

@tindex unless
@table @asis
@item @samp{(unless EXPR CMDS)}
Run @samp{CMDS} only if @samp{EXPR} is nil.  This is the @code{loopy}
version of the @code{unless} macro from normal Emacs Lisp.

@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 lisp
@end table

@node Skipping Cycles
@subsection Skipping Cycles

@dfn{Skip commands} are used to skip iteration cycles in the loop@comma{}
immediately jumping to the next iteration.  They are equivalent to the
@code{continue} statement used by Python and other languages.  For this reason@comma{} they
have aliases that use the word ``continue'' instead of the word ``skip''.

@tindex skip
@tindex continue
@tindex skipping
@tindex continuing
@table @asis
@item @samp{(skip|continue)}
Skip the remaining commands and continue to the next loop
iteration.

This command also has the aliases @samp{skipping} and @samp{continuing}.

@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 lisp
@end table

@tindex skip-from
@tindex continue-from
@tindex skipping-from
@tindex continuing-from
@table @asis
@item @samp{(skip-from|continue-from NAME)}
Skip the remaining commands and continue
to the next loop iteration of the loop @samp{NAME}.

This command also has the aliases @samp{skipping-from} and @samp{continuing-from}.

@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 lisp
@end table

@node Early Exit
@subsection Early Exit

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

In Loopy@comma{} implied accumulation variables can be modified a final time after the
loop exits in order to finalize their values.  For example@comma{} if an optimized
accumulated list is built in reverse@comma{} 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@comma{} allowing values to be
finalized.

@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 lisp

As noted in @ref{Special Macro Arguments}@comma{} the return value of Lisp code given in the special
macro argument @samp{finally-do} does not affect the return value of the loop.
However@comma{} @samp{finally-do} can modify the values of variables after the loop
completes@comma{} including @code{loopy-result} (even when used as an implied accumulation
variable).

@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'@comma{}
        ;; but because of the `return' command@comma{}
        ;; 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 lisp

As noted in @ref{Special Macro Arguments}@comma{} the special macro argument @samp{finally-return}
overrides the return value of the loop@comma{} including values that would have been
returned by any ``return'' commands.

@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 lisp


@tindex leave
@tindex leaving
@table @asis
@item @samp{(leave)}
Leave the current loop without forcing a return value.

This command also has the alias @samp{leaving}.

@lisp
;; => (1 2 3 4)
(loopy (list i '(1 2 3 4 5 6 7))
       (if (= i 5)
           (leave)
         (collect i)))
@end lisp
@end table

@tindex leave-from
@tindex leaving-from
@table @asis
@item @samp{(leave-from NAME)}
Leave the loop @samp{NAME} without forcing a return value.
This command is equivalent to @samp{(at NAME (leave))} (@ref{Sub-Loops}).

This command also has the alias @samp{leaving-from}.

@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 lisp
@end table

@tindex return
@tindex returning
@table @asis
@item @samp{(return [EXPRS])}
Return from the current loop without finalizing values@comma{}
returning @samp{[EXPRS]}.

This command also has the alias @samp{returning}.

@lisp
;; => 6
(loopy (with  (j 0))
       (do (cl-incf j))
       (when (> j 5)
         (return j)))
@end lisp
@end table

@tindex return-from
@tindex returning-from
@table @asis
@item @samp{(return-from NAME [EXPRS])}
Return from the loop @samp{NAME}@comma{} returning
@samp{[EXPRS]}.  This command is equivalent to @samp{(at NAME (return))} (@ref{Sub-Loops}).

This command also has the alias @samp{returning-from}.

@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 lisp
@end table

@tindex while
@table @asis
@item @samp{(while COND)}
Leave the loop once @samp{COND} is false@comma{} without forcing a
return value.  @samp{(while COND)} is the same as @samp{(until (not COND))}
and @samp{(unless COND (leave))}.

@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 lisp
@end table

@tindex until
@table @asis
@item @samp{(until COND)}
Leave the loop once @samp{COND} is true@comma{} without forcing a return
value.  @samp{(until COND)} is the same as @samp{(while (not COND))}
and @samp{(when COND (leave))}.

@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 lisp
@end table

@node Sub-Loops
@section Sub-Loops

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

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

@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 lisp

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

@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 lisp

@quotation Warning
Don't confuse using these sub-loop commands with using calls to the macros
@code{loopy} and @code{loopy-iter}.  For example@comma{} the @samp{EXPR} parameter to loop commands is
used literally@comma{} and is not guaranteed to be able to affect macro expansion.

@end quotation

The commands are described in more detail below.

@tindex loopy
@table @asis
@item @samp{(loopy [SPECIAL-MACRO-ARGUMENTS or CMDS])}
Use the @code{loopy} macro as a
loop command.

@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 lisp
@end table

@tindex loopy-iter
@table @asis
@item @samp{(loopy-iter [SPECIAL-MACRO-ARGUMENTS or CMDS or LISP-EXPRS])}
Use the
@code{loopy-iter} macro as a loop command (@ref{The @code{loopy-iter} Macro}).

This feature can only be used after first loading the library @samp{loopy-iter}.

@lisp
(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 lisp
@end table

@tindex at
@table @asis
@item @samp{(at LOOP-NAME [CMDS])}
Parse commands with respect to the super-loop
@samp{LOOP-NAME}.  For example@comma{} a @samp{leave} subcommand would exit the super-loop
@samp{LOOP-NAME}@comma{} and an accumulation command would create a variable in that
super-loop.

If one did not use @samp{at} in the below example@comma{} then the accumulation would be
local to the sub-loop and the return value of the loop @samp{outer} would be @code{nil}.

@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 lisp

Keep in mind that the effects of flags (@ref{Using Flags}) are local to the loops in
which they are used@comma{} even when using the @samp{at} command.

@lisp
;; => ((1 2 11 12)
;;     ((2) (3) (12) (13)))
(loopy outer
       (flag pcase)
       (array elem [(1 2) (11 12)])
       (collect `(@comma{}first . @comma{}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 lisp
@end table

@node Destructuring Macros
@chapter Destructuring Macros

@cindex destructuring macros
The below macros make Loopy's built-in destructuring system available for
general use (@ref{Basic Destructuring}).  For example@comma{} @code{loopy-let*} can be used for
@code{let}-binding destructured values@comma{} similar to @code{pcase-let*}.  Because libraries
like @samp{pcase} and @samp{seq} already provide similar destructuring macros using their
own destructuring systems@comma{} these macros are not affected by the flags
that configure the destructuring used by loop commands (@ref{Using Flags})@comma{} as that would
be redundant.

To be clear@comma{} these destructuring macros can be used outside of the looping
macros.

@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 lisp

@findex loopy-let*
@table @asis
@item @code{loopy-let*}
Use destructuring in a @code{let} form@comma{} like in @code{pcase-let*} and
@code{seq-let}.

@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 lisp
@end table

@findex loopy-setq
@table @asis
@item @code{loopy-setq}
Use destructuring in a @code{setq} form@comma{} like in @code{seq-setq} and
@code{pcase-setq}.

@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 lisp
@end table

@findex loopy-lambda
@table @asis
@item @code{loopy-lambda}
Use destructuring in a @code{lambda}'s argument list@comma{} like in
@code{pcase-lambda} and @code{cl-function}.

@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 lisp
@end table

@findex loopy-ref
@table @asis
@item @code{loopy-ref}
Create destructured references to the fields in a sequence via
@code{cl-symbol-macrolet}.  Do not confuse this with the behavior of @code{cl-letf}@comma{}
which temporarily binds those places to a value.

This macro uses the destructuring found in the sequence-reference iteration
commands (@ref{Sequence Reference Iteration}).  There are some limitations to this
functionality in Emacs Lisp@comma{} which are described in that section.

@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 lisp
@end table

@node The @code{loopy-iter} Macro
@chapter The @code{loopy-iter} Macro

@cindex loopy-iter
@findex loopy-iter
@code{loopy-iter} is a macro that allows the embedding of loop commands inside of
arbitrary code.  This is different from the loop command @samp{do}@comma{} which allows the
embedding of arbitrary code inside of a @code{loopy} loop.  You must use @code{require} to
load this feature.

This macro is named after the @code{iterate} or @code{iter} macro provided by the Common
Lisp package ``Iterate'' @footnote{@uref{https://common-lisp.net/project/iterate/}} (not to be confused with the @code{iter-*} functions
provided by Emacs).  However@comma{} while @code{loopy} and @code{loopy-iter} were influenced by
@code{iterate}@comma{} @code{loopy-iter} is not a port of @code{iterate} to Emacs Lisp.

@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'@comma{} 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 lisp

@cindex loopy-iter name conflicts
The arguments of @code{loopy} are limited to loop commands and special macro
arguments.  @code{loopy-iter} differs by allowing arbitrary Lisp expressions@comma{} in
which loop commands are treated as macros to be expanded by @code{macroexpand-all}.
Hence@comma{} 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
@code{cl-loop}@comma{} to an extent)@comma{} almost all commands in @code{loopy} have aliases in the
present-participle form (the ``-ing'' form).  For example@comma{} Loopy provides the
command @samp{list} with the alias @samp{listing}.  Because the command name @samp{list} would
conflict with the built-in Emacs Lisp function @code{list}@comma{} only the command name
@samp{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 @ref{Default Bare Names in @code{loopy-iter}}.

@lisp
;; In `loopy'@comma{} `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'@comma{} `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 lisp

The non-conflicting names of loop commands and special macro arguments
recognized by @code{loopy-iter} can be customized with the user option
@code{loopy-iter-bare-names}@comma{} which is a list of symbols naming commands@comma{} special
macro arguments@comma{} and their aliases.  Again@comma{} these symbols are found in the loop
body by using Emacs Lisp's macro-expansion features@comma{} so adding a name that
overrides a symbol's function definition can cause errors. Names that are
obvious conflicts@comma{} such as the non-suffixed versions of most loop commands (such
as @samp{list})@comma{} loop commands that are unneeded in @code{loopy-iter} (such as @samp{when})@comma{}
and the names of Loopy features that are the same as the names of Emacs Lisp
features (such as @samp{let*} for @samp{with}) are by default excluded from
@code{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@comma{} one can write the feature name preceded
by one of the keywords in @code{loopy-iter-keywords}.  This is the second method.

By default@comma{} the possible keywords are @samp{for}@comma{} @samp{accum}@comma{} @samp{exit}@comma{} and @samp{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@comma{} such as the @samp{:test} in @code{(cl-count ITEM SEQ :test
FUNC)}.  For example@comma{}

@itemize
@item
the command @samp{(listing VAR LIST)} could also be written as @samp{(for list VAR
  LIST)} and as @samp{(for listing VAR LIST)}.
@item
the special marco argument @samp{with}@comma{} with the alias @samp{let*}@comma{} could also be
written as @samp{(arg let* (VAR EXPR) ...)}.  See that this does not conflict with
using the @code{let*} special form.
@end itemize


This method recognizes all commands and their aliases in the user option
@code{loopy-parsers}.

@float Listing
@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'@comma{} 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 lisp
@caption{The first example@comma{} but now using keyword symbols.}
@end float

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

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

@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 lisp

@cindex loopy-iter sub-loops
In the macro @code{loopy}@comma{} the commands @samp{loopy} and @samp{loopy-iter} are needed to
correctly handle sub-loops.  Those commands are not needed in the macro
@code{loopy-iter}@comma{} since the macro expands any macros in its argument while
processing them.

@lisp
;; => (2 3 4 5)
(loopy-iter outer
            (listing i '([1 2] [3 4]))
            ;; NOTE: `loopy-iter' macro@comma{} 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@comma{} not command
            (loopy (array j i)
                   (set val (1+ j))
                   (at outer (collect val))))
@end lisp

Finally@comma{} there are a few things to keep in mind when using @code{loopy-iter}:

@enumerate
@item
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.

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

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

Ideally@comma{} such problematic macros are uncommon.  Please report such cases on
this project's @uref{https://codeberg.org/okamsn/loopy/issues, issues tracker} so that they can be added to
@code{loopy-iter-suppressed-macros} by default.
@end enumerate

@menu
* Default Bare Names in @code{loopy-iter}::
@end menu

@node Default Bare Names in @code{loopy-iter}
@section Default Bare Names in @code{loopy-iter}

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

By default@comma{} the following commands are not recognized:
@itemize
@item
@samp{do} and @samp{command-do}@comma{} which are not needed.

@item
Commands that exists only as a command version of an existing Lisp feature@comma{}
such as @samp{if}@comma{} @samp{cond}@comma{} @samp{when}@comma{} and @samp{unless}.

@item
The commands @samp{loopy} and @samp{loopy-iter}.  The macro @code{loopy-iter} expands macros
in its arguments@comma{} and so can properly handle instances of itself and the macro
@code{loopy} without needing them to be reimplemented as loop commands.

@item
@samp{while}@comma{} as it would conflict with the special form @code{while}.
Use @code{(when (not COND) (leaving))} or @code{(unless COND (leaving))} instead.

@item
@samp{until}@comma{} to be consistent with the exclusion of @samp{while}.
@end itemize


Using the commands @samp{returning} and @samp{returning-from} are the same as using the
macros @code{cl-return} and @code{cl-return-from}@comma{} except that the commands automatically
create a list if more than one return value is given.

@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 lisp


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

@node Customizing Macro Behavior
@chapter Customizing Macro Behavior

There are three ways to control behavior:

@enumerate
@item
Setting a flag (@ref{Using Flags})@comma{} which currently controls destructuring
and whether the generated code uses a @code{while}-loop.
@item
Modifying the customizable variables like @code{loopy-parsers} and
@code{loopy-iter-bare-names}
@item
Using the special macro argument @samp{override}@comma{} which modifies the same
settings as the customizable variables@comma{} but only locally for the
current loop (and not for sub-loops)
@end enumerate

A package which extends Loopy and wishes to make those extensions available to
others would modify the customizable variables@comma{} 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
@samp{override} special macro argument within a bespoke macro that wraps Loopy.

@menu
* Using Flags::                  Using flags to change behavior.
* Custom Aliases::               How to add one's own aliases.
* Custom Commands::              Extending `loopy' with personal commands.
* Locally Overriding Behavior::  Using the `override' special macro argument.
@end menu

@node Using Flags
@section Using Flags

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

Flags are applied in order.  If you specify @samp{(flags seq pcase)}@comma{} then @code{loopy}
will use destructuring like @code{pcase-let}@comma{} not like @code{seq-let}.

The following flags are currently supported:

@cindex pcase flag
@cindex seq flag
@cindex dash flag
@cindex no-loop flag
@cindex default flag
@table @asis
@item @samp{pcase}
Use @code{pcase-let} for destructuring
(@ref{Destructuring with pcase Patterns,,,elisp,}).
@item @samp{seq}
Use @code{seq-let} for destructuring (@ref{seq-let,,,elisp,}).
@item @samp{dash}
Use the style of destructuring found in the @samp{dash} library
(@ref{-let,,,dash,}).
@item @samp{no-loop} 
Expand the looping macros without creating a @code{while}-loop.  This
is intended for wrapping macros that don't want to work around the default
@code{while}-loop.
@item @samp{default}
Use the default behavior for all options.
@end table


For convenience@comma{} all flags (except @samp{default}) can be undone by prefixing them
with @samp{-} (a dash or minus sign)@comma{} which reverts @code{loopy} to its default behavior.

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

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

@menu
* Destructuring Flags::          Using different destructuring systems.
* The @samp{no-loop} Flag::      Avoiding creating the loop.
@end menu

@node Destructuring Flags
@subsection Destructuring Flags

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

@table @asis
@item @samp{pcase}
Use @code{pcase-let} for destructuring
(@ref{Destructuring with pcase Patterns,,,elisp,}).
@item @samp{seq}
Use @code{seq-let} for destructuring (@ref{seq-let,,,elisp,}).
@item @samp{dash}
Use the style of destructuring found in the @samp{dash} library
(@ref{-let,,,dash,}).
@item @samp{default}
Use the default behavior for all options.
@end table


@cindex loopy-dash
@cindex loopy-pcase
@cindex loopy-seq
The destructuring flags (@samp{pcase}@comma{} @samp{seq}@comma{} and @samp{dash}) are separate libraries
(respectively@comma{} @samp{loopy-pcase}@comma{} @samp{loopy-seq}@comma{} and @samp{loopy-dash}) that must be
loaded after @samp{loopy}.  Currently@comma{} @samp{loopy-dash} is a separate package.

Below are some examples of using the destructuring flags.  These flags affect
the destructuring of:
@itemize
@item
iteration variables
@item
accumulation variables
@item
variables explicitly named by the commands @samp{set} and @samp{set-prev}
@item
variables bound by the special macro argument @samp{with}
@end itemize

These flags do not affect the destructuring of generalized variables
(@code{setf}-able places) as the libraries @samp{pcase.el}@comma{} @samp{seq.el}@comma{} and @samp{dash.el} do not
yet provide the required functionality.

@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 `[@comma{}v1 @comma{}v2] `(@comma{}v3 @comma{}v4))
              [10 20]))
       (list elem '((1 (2 3)) (4 (5 6))))
       (collect `(@comma{}a (@comma{}_ @comma{}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 lisp

@quotation Warning
For accumulation commands@comma{} 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@comma{} which can be interpreted as accumulation
variables.

@end quotation

For the warning@comma{} consider the below example in which a hypothetical @code{pcase}
pattern creates the variable @code{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@comma{} @code{temporary?} is treated as a user-facing accumulation
variable.  Such cases can be unwanted and produce inefficient code.

@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 lisp

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

@lisp
(require 'loopy-pcase)
(defmacro my-loopy-flag-wrapper (&rest body)
  "Use `loopy'@comma{} 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 `(@comma{}i . @comma{}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 `(@comma{}i . @comma{}j) '((1 . 2) (3 . 4)))
                       (collect i)
                       (collect j))
@end lisp

@node The @samp{no-loop} Flag
@subsection The @samp{no-loop} Flag

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 @code{dolist} or @code{mapc}).  By default@comma{} Loopy creates an infinite
@code{while}-loop@comma{} which can interfere with this approach.

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

@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 lisp

@cindex no-loop flag
Another way to avoid the @code{while}-loop is to use the flag @samp{no-loop}.  The benefit
of using the @samp{no-loop} flag is that@comma{} because its meaning is clear during macro
expansion@comma{} it can trigger additional error checking.  Currently@comma{} the @samp{no-loop}
flag will cause Loopy to signal an error when used with the following features:
@itemize
@item
Iteration commands like @samp{list} or @samp{array}
@item
Commands like @samp{skip}
@end itemize

@lisp
;; `listing' doesn't make sense with `no-loop'@comma{} so Loopy
;; signals an error here:
;;
(loopy-iter (flag no-loop)
            (listing i '(1 2 3 4))
            (summing i))
@end lisp

Currently@comma{} all other Loopy features can be used with the @samp{no-loop} flag@comma{}
including:
@itemize
@item
Special macro arguments
@item
Accumulation commands@comma{} such as @samp{sum} and @samp{collect}
@item
Early-exit commands@comma{} such as @samp{leave} and @samp{return}
@item
sub-loop commands
@end itemize

One might wish to use this feature to implement something like @code{cl-block} using
@code{loopy-iter}@comma{} as in the below example.

@lisp
(defmacro my-loopy-block (&rest args)
  `(loopy-iter (flag no-loop)
               @comma{}@@(butlast args)
               (finally-return @comma{}(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 lisp

@node Custom Aliases
@section Custom Aliases

@cindex custom aliases
An @dfn{alias} is another name for a loop command (@ref{Loop Commands}) or
special macro argument (@ref{Special Macro Arguments}).  @code{loopy} comes with several built-in
aliases@comma{} such as the alias @samp{string} for the command @samp{array} and the alias @samp{else}
for the special macro argument @samp{after-do}.

@multitable {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaaaaaaa}
@headitem Command or Special Macro Argument
@tab Built-In Aliases
@item @samp{array}
@tab @samp{string}
@item @samp{numbers}
@tab @samp{number}
@item @samp{after-do}
@tab @samp{after}@comma{} @samp{else}@comma{} @samp{else-do}
@end multitable

Using an alias works the same as using the preferred name of the command or
special macro argument.  Implementation-wise@comma{} 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.

@float Listing
@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 lisp
@caption{`array' and `string' are different names for the same command.}
@end float

@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 @code{loopy-parsers} with the appropriate parsing
function.  Using an alias does not change the fact that special macro arguments
are parsed before loop commands.

@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 lisp

@findex loopy-defalias
Previously@comma{} the macro @code{loopy-defalias} was used to modify the now-deprecated
user option @code{loopy-aliases}.  Now@comma{} it modifies @code{loopy-parsers}.  This change
should be unnoticed by users.  @code{loopy-defalias} remains a forward-compatible way
of creating aliases.

@quotation Warning
For portability reasons@comma{} you should not globally override any of the existing
names@comma{} as that might create conflicts during macro expansion with other code
that uses the macro and relies on those names.  Instead@comma{} use the
special macro argument @samp{override} (@ref{Locally Overriding Behavior}) to create local aliases.

@end quotation

@node Custom Commands
@section Custom Commands

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

@menu
* Background Info::              The internals of `loopy'.
* Hello World::                  A minimal working example.
* An @code{always} Command::     Adding a feature from `cl-loop'.
* Custom commands in the @code{loopy-iter} macro::
* Finding More Examples::
@end menu

@node Background Info
@subsection Background Info

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

Some examples of instructions are:

@itemize
@item
Declaring a given variable in a @code{let}-like form to make sure it's locally
scoped.

@item
Declaring a generated variable in a @code{let}-like form to contain a given value.

@item
Adding a condition for continuing/exiting the loop.

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

@item
Adding code to be run after the main processing section@comma{} such as for updating
variables.  This location is referred to as the @dfn{latter body} of the
loop.
@end itemize


For example@comma{} parsing the command @samp{(list i '(1 2 3))} produces the following list
of instructions.  Some commands require the creation of unique temporary
variables@comma{} such as @samp{list-211} in the below output.

@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 lisp

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@comma{}
the above instruction referencing @code{loopy--iteration-vars} inserts a binding for
the variable @samp{list-211} into a @code{let}-like form.

@multitable {aaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}
@headitem Place
@tab Code
@item @samp{loopy--iteration-vars}
@tab @samp{(list-211 '(1 2 3))}
@item @samp{loopy--latter-body}
@tab @samp{(setq list-211 (cdr list-211))}
@item @samp{loopy--pre-conditions}
@tab @samp{(consp list-211)}
@item @samp{loopy--iteration-vars}
@tab @samp{(i nil)}
@item @samp{loopy--main-body}
@tab @samp{(setq i (car list-211)))}
@end multitable

@cindex instruction order
Instructions are applied in order in such a way that earlier instructions are
@emph{not} overridden by later instructions.  For example@comma{} if the special macro
argument @samp{with} sets a variable's value@comma{} that value will not be overridden by
commands which might try to initialize that variable to @code{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 @code{loopy--parse-loop-command}@comma{} which receives a command
call@comma{} such as @samp{(list i '(1 2 3))}@comma{} and returns a list of instructions.  It does
this by searching for an appropriate command-specific parsing function in
@code{loopy-parsers}.  For parsing multiple commands in order@comma{} there is
@code{loopy--parse-loop-commands}@comma{} which wraps the single-command version.

For example@comma{} consider the function @code{loopy--parse-if-command}@comma{} which parses the
@samp{if} loop command.  It needs to check the instructions of the sub-commands
passed to @samp{if}@comma{} looking for code that would be inserted into the loop's main
body (as determined by the first element of the instruction).  Once found@comma{} it
wraps that code with an @code{if}-form.

@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 lisp

For the purpose of this example@comma{} 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}.

@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@comma{}
    ;; 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@comma{}
      ;;       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@comma{} that means wrapping multiple instructions in
      ;; a `progn' form.  For others@comma{} 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@comma{} full list of instructions.
      `((loopy--main-body (if @comma{}condition
                              @comma{}if-true-main-body
                            @comma{}@@(nreverse if-false-main-body)))
        @comma{}@@(nreverse full-instructions)))))
@end lisp

In general@comma{} the code of command parsers is not complicated.  The hardest part of
writing the parser is making sure that the code inserted into the @code{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
@table @asis
@item @samp{loopy--generalized-vars}
Lists of a symbol and a macro expansion that will
be given to @samp{cl-symbol-macrolet}.  This is used to create named @code{setf}-able
places.  The expansion you use depends on the kind of sequence and how the it
is updated.

For example@comma{} @samp{(list-ref i my-list)} declares @samp{i} to be a symbol which expands
to @samp{(car TEMP-VAR)}@comma{} in which @samp{TEMP-VAR} holds the value of @samp{my-list}.  At the
end of the loop body@comma{} @samp{TEMP-VAR} is set to its @samp{cdr}@comma{} ensuring that the next
call to @samp{car} returns the correct value.
@end table

@vindex loopy--iteration-vars
@table @asis
@item @samp{loopy--iteration-vars}
Lists of a symbol and an expression that will be
given to @code{let*}.  This is used for initializing variables needed for iteration
commands@comma{} such as the @samp{i} in @samp{(list i '(1 2 3))} or to store the list @samp{'(1 2
  3)} in @samp{(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@comma{} such as if the variable was used by multiple
iteration commands@comma{} that could result in expanding into incorrect code which
would fail during runtime
@end table

@vindex loopy--accumulation-vars
@table @asis
@item @samp{loopy--accumulation-vars}
Lists of a symbol and an expression that will be
given to @code{let*}.  This is used for initializing variables needed for
accumulation commands@comma{} such as the @samp{coll} in @samp{(collect coll my-val)} or any
variables needed for destructuring for said commands.
@end table

@vindex loopy--other-vars
@table @asis
@item @samp{loopy--other-vars}
Lists of a symbol and an expression that will be
given to @code{let*}.  This is used for initializing variables needed for
generic commands@comma{} such as the @samp{my-var} in @samp{(set my-var 2)} or any
variables needed for destructuring for said command.
@end table

@vindex loopy--pre-conditions
@table @asis
@item @samp{loopy--pre-conditions}
Expressions that determine if the @samp{while} loop
runs/continues@comma{} such as whether a list still has elements in it.  If there is
more than one expression@comma{} than all expressions are used in the Emacs Lisp
special form @code{and}.
@end table

@vindex loopy--main-body
@table @asis
@item @samp{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@comma{} for
example@comma{} the @code{dolist} macro.  Code that can be placed in the main body
includes:
@itemize
@item
code that updating some iteration variables@comma{} such as the @samp{i} in @samp{(list i
    my-list)}
@item
code that updating accumulation variables@comma{} such as the @samp{coll} in @samp{(collect
    coll my-value)}
@item
code that exits the loop early@comma{} such as from the @samp{leave} command
@end itemize
@end table

@vindex loopy--latter-body
@table @asis
@item @samp{loopy--latter-body}
Expressions that need to be run after the main body@comma{}
such as updating some of the variables that determine when a loop ends.  For
example@comma{} for the command @samp{(list i my-list)}@comma{} the command applies the function
that moves the command through the list (by default@comma{} @code{cdr}) in the loop's
latter body@comma{} so that during the next iteration of the main body the iteration
variable @samp{i} can be set to the @code{car} of that @code{cdr}.
@end table

@vindex loopy--post-conditions
@table @asis
@item @samp{loopy--post-conditions}
Expressions that determine whether the @samp{while}
loop continues@comma{} but checked after the loop body has run.  The code from this
is ultimately appended to the latter body before being substituted in.
@end table

For accumulation commands@comma{} you might also wish to place values in the following:

@vindex loopy--implicit-return
@table @asis
@item @samp{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@comma{} and in some
special other cases.

By default@comma{} the implicit return value is @code{loopy-result}@comma{} and so this variable
is usually just a list of the symbol @samp{loopy-result}.
@end table

@vindex loopy--vars-final-updates
@table @asis
@item @samp{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:
@enumerate
@item
Correcting the order of elements in a list that was constructed in reverse.
@item
Coercing the variable into a new sequence type.
@item
Appending a list of lists.
@end enumerate

Each accumulation variable can only be updated once@comma{} in a single way.  For
example@comma{} 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@comma{}
including if they require no final update.
@end table


Loopy will attempt to produce efficient code@comma{} and will not attempt to set up
features which are not used.  Therefore@comma{} the expanded code depends on the kinds
of instructions that are returned by the parsing functions.  For the most part@comma{}
the instructions affect the expansion of the loop that contains their respective
command.  However@comma{} there are cases where a command must send instructions to a
surrounding loop@comma{} not just the loop which immediately contains it.  Consider
using an accumulation command within the @samp{at} command@comma{} as in the below example.
The accumulation variable must be declared for the loop @samp{outer}@comma{} but the
accumulation itself must still occur within the loop @samp{inner}.

@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 lisp

To communicate these instructions@comma{} use @code{loopy--at-instructions}.  For example@comma{}
the output of parsing @samp{(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 @samp{collect} command@comma{} not all are sent to the loop @samp{outer}.

@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 lisp

@vindex loopy--at-instructions
@table @asis
@item @samp{loopy--at-instructions}
Instructions that should be interpreted by a
surrounding loop.  For example@comma{} this kind of instruction is used by the @samp{at}@comma{}
@samp{skip-from}@comma{} and @samp{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@comma{} the
instructions affect the innermost surrounding loop of that name.
@end table


There are 4 more variables a loop command can push to@comma{} 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@comma{} for example@comma{} you are
concerned with what happens after the loop exits/completes.

@vindex loopy--before-do
@table @asis
@item @samp{loopy--before-do}
Expressions to evaluate before the loop.  These are
derived from the @samp{before-do} macro argument.
@end table

@vindex loopy--after-do
@table @asis
@item @samp{loopy--after-do}
Expressions to evaluate after the loop completes
successfully.  These are derived from the @samp{after-do} macro argument.
@end table

@vindex loopy--final-do
@table @asis
@item @samp{loopy--final-do}
Expressions to evaluate after the loop completes@comma{}
regardless of success.  These are derived from the @samp{finally-do} macro
argument.
@end table

@vindex loopy--final-return
@table @asis
@item @samp{loopy--final-return}
An expression that is always returned by the macro@comma{}
regardless of any early returns in the loop body.  This is derived from the
@samp{finally-return} macro argument.
@end table


@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 @code{loopy--loop-name}.  The default name is @code{nil}.

The structure of the macro’s expanded code depends on the features used (for
example@comma{} @code{loopy} won’t try to declare variables if none exist)@comma{} but the result
will work similar to the below example.

@lisp
`(cl-symbol-macrolet @comma{}loopy--generalized-vars
   (let* @comma{}loopy--with-vars
     (let @comma{}loopy--accumulation-vars
       (let* @comma{}loopy--iteration-vars
         (let ((loopy--early-return-capture
                (cl-block @comma{}loopy--loop-name
                  @comma{}@@loopy--before-do
                  (catch loopy--non-returning-exit-tag-name
                    (while @comma{}(cl-case (length loopy--pre-conditions)
                              (0 t)
                              (1 (car loopy--pre-conditions))
                              (t (cons 'and loopy--pre-conditions)))
                      (catch loopy--skip-tag-name
                        @comma{}@@loopy--main-body)
                      @comma{}@@loopy--latter-body
                      (unless @comma{}loopy--post-conditions
                        (cl-return-from @comma{}loopy--loop-name
                          @comma{}loopy--implicit-return)))
                    @comma{}loopy--vars-final-updates
                    @comma{}@@loopy--after-do))
                @comma{}loopy--implicit-return))
           @comma{}@@loopy--final-do
           @comma{}(if loopy--final-return
                loopy--final-return
              'loopy--early-return-capture))))))
@end lisp

@node Hello World
@subsection Hello World

This is an example of a command that prints a message during the main loop body.

To implement a custom loop-body command@comma{} Loopy needs two pieces of information:
@enumerate
@item
The keyword that names your command
@item
The parsing function that can transform occurences of your command into
instructions.
@end enumerate

Importantly@comma{} as with Emacs Lisp functions@comma{} there can only be one command with a
given name.

For example@comma{} say that you're tired of typing out @samp{(do (message "Hello@comma{} %s %s"
PERSONAL-NAME FAMILY-NAME))} and would prefer to instead use @samp{(greet
PERSONAL-NAME [FAMILY-NAME])}.  This only requires adding code to the loop's
main body@comma{} so the definition of the parsing function is quite simple.

@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@comma{} see also `cl-with-gensyms' and `cl-once-only'@comma{}
  ;; also available from the Compat library
  ;; (https://elpa.gnu.org/packages/compat.html)@comma{} on which Loopy depends.
  (let ((family-name-var (gensym "family-name")))
    `((loopy--main-body
       (if-let ((@comma{}family-name-var @comma{}family-name))
           (message "Hello@comma{} %s %s" @comma{}personal-name @comma{}family-name-var)
         (message "Hello@comma{} %s" @comma{}personal-name))))))
@end lisp

Loopy will pass the entire command expression to the parsing function@comma{} and
expects that a list of instructions will be returned.

@vindex loopy-parsers
To tell Loopy about this function@comma{} add it and the command name @samp{greet} to the
variable @code{loopy-parsers}@comma{} which associates commands with parsing
functions.  The function that is paired with the symbol receives the entire
command expression@comma{} and should produce a list of valid instructions.

@lisp
;; Using the Map library@comma{} for convenience:
(require 'map)
(setf (map-elt loopy-parsers 'greet)
      #'my-loopy-greet-command-parser)
@end lisp

After that@comma{} you can use your custom command in the loop body.

@lisp
(loopy (list name '(("John" "Deer") ("Jane" "Doe") ("Jimmy")))
       (greet (car name) (cadr name)))
@end lisp

By running @kbd{M-x pp-macroexpand-last-sexp @key{RET}} on the above expression@comma{}
you can see that it expands to do what we want@comma{} as expected.

@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@comma{} %s %s"
                   (car name)
                   family-name900)
        (message "Hello@comma{} %s"
                 (car name)))
      (setq list-166 (cdr list-166)))
    nil))
@end lisp

@node An @code{always} Command
@subsection An @code{always} Command

Lets say we want to emulate @code{cl-loop}'s @samp{always} clause@comma{} which causes the loop
to return @code{nil} if an expression evaluates to @code{nil} and @code{t} otherwise.  This is
similar to the functions @code{cl-every} and @code{seq-every-p}.

Here is an example:

@lisp
;; => t
(cl-loop for i from 1 to 9 always (< i 10))
@end lisp

While @code{loopy} already has an @samp{always} command@comma{} we'll ignore it for the sake of
this example.  Without a custom command@comma{} you could translate this using the
following code:

@lisp
;; => t
(loopy (numbers i :from 1 :to 9)
       (unless (< i 10) (return nil))
       (else-do (cl-return t)))
@end lisp

This is similar to what you might write in other languages@comma{} such as Python.

@example
# In some testing Python function:
for i in range(1@comma{} 10):
    if not (i < 10):
        return False
else:
    return True
@end example

While the meaning of the code is clear@comma{} this approach is certainly wordier.

Here's how one could do this using a custom command.  Again@comma{} Loopy already
comes with a built-in @samp{always} command.  This example is taken directly from the
file @file{loopy-commands.el}@comma{} which contains the code of all of @code{loopy}'s
built-in parsers.

We can describe the command's desired behavior in two sentences:
@enumerate
@item
The loop should immediately return @code{nil} if the expression ever evaluates to
@code{nil}.
@item
The loop should return @code{t} if the loop is able to complete successfully.
@end enumerate

This simplest way to satisfy the first requirement is to conditionally use
@code{cl-return} if the expressions ever evaluates to @code{nil}.  We want to do this
while the loop is running@comma{} so we should use an instruction for
@code{loopy--main-body}.

@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 @comma{}CONDITION (cl-return nil)))

;; in which CONDITION is supplied by the parsing function.
@end lisp

For a simple loop@comma{} this works well enough.  However@comma{} this approach has two
problems:
@enumerate
@item
If the loop is named something other than @samp{nil}@comma{} @code{cl-return} would return
from the wrong @code{cl-block}.
@item
Using @code{cl-return} directly causes the loop to exit immediately without
finalizing optimized accumulation values@comma{} so we wouldn't be able to correctly
return multiple values from the macro.
@end enumerate

The first problem could be solved by using the variable @code{loopy--loop-name} to
retrieve the symbol naming the current loop@comma{} but that still leaves the second
problem.  The better solution@comma{} when the condition is no longer meant@comma{} is to
store a @code{nil} value in an accumulation command and to then leave the loop while
still finalizing variables@comma{} such as via the @samp{leave} command.  This gives us
something like:

@lisp
`(loopy--main-body (unless @comma{}CONDITION
                     (setq @comma{}ACCUM-VAR nil)
                     @comma{}@@(LEAVE-COMMAND-CODE)))
@end lisp

The best way to satisfy the second requirement is to set the initial value of
the accumulation variable to @code{t} and to make that accumulation variable the
implied return value of the loop.  As described in @ref{Early Exit}@comma{}
leaving a loopy early (such as via the @samp{leave} command) does not change the
implied return value of a loop.  The logic of the command would be:
@enumerate
@item
Initialize the accumulation variable to @code{t}.
@item
During the loop@comma{} if the condition is @code{nil}@comma{} immediately set the accumulation
variable to @code{nil} and leave the loop.  If the condition is always non-@code{nil}@comma{}
then we need do nothing.
@item
Regardless of what happens during the loop@comma{} the implied return value is used
as the return value of the macro@comma{} barring any use of the @samp{finally-return}
macro argument.
@end enumerate

With that in mind@comma{} our instructions for the loop would be

@lisp
`((loopy--accumulation-vars (@comma{}ACCUM-VAR t))
  (loopy--implicit-return @comma{}ACCUM-VAR)
  (loopy--main-body (unless @comma{}COND
                      (setq @comma{}ACCUM-VAR nil)
                      @comma{}@@(LEAVE-COMMAND-CODE))))
@end lisp

Once we've chosen our instructions@comma{} we need to tell Loopy what function to use
to produce these instructions.  Like in the previous example@comma{} we define the
parsing function and add it to @code{loopy-parsers}.

@lisp
;; As noted in the previous section@comma{} the parsing function is always
;; passed the entire command as `(always CONDTION)'@comma{} 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@comma{} `loopy' should immediately return nil.
Otherwise@comma{} `loopy' should return t."
  (let ((result-var 'loopy-result))
    `((loopy--accumulation-vars (@comma{}result-var t))
      (loopy--implicit-return @comma{}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.
      @comma{}@@(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
                `(@comma{}@@other-instrs
                  (loopy--main-body (unless @comma{}condition
                                      (setq @comma{}result-var nil)
                                      @comma{}@@main-exprs))))))))

(setf (map-elt loopy-parsers 'always)
      #'my--loopy-always-command-parser)
@end lisp

Once we've added our parsing function to @code{loopy-parsers}@comma{} Loopy will use that
function whenever it tries to understand the @samp{always} command.  In this case@comma{}
this custom parser would supercede the built-in parser.  An example output of
parsing an example command would be:

@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 lisp

Here are some examples of the command in action:

@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 lisp

The actual definition of the @samp{always} command can be read in the library
@file{loopy-commands.el}.  It is close to the above definition@comma{} but
includes more error checking for working with the other commands.

@node Custom commands in the @code{loopy-iter} macro
@subsection Custom commands in the @code{loopy-iter} macro

See also @ref{The @code{loopy-iter} Macro}.

@code{loopy-iter} works by:
@enumerate
@item
separating main-body instructions from other instructions
@item
expanding that main-body code for @code{loopy} sub-commands
@item
collected those expansions into the outputted loop's body
@item
processing the collected non-main-body instructions
@end enumerate

Most loop commands should work with @code{loopy-iter} without any changes.  Some loop
commands are just @code{loopy} versions of built-in Lisp macros or special forms@comma{} and
so aren't needed in @code{loopy-iter} in the first place.

However@comma{} special consideration is needed for commands that produce main-body
code outside of main-body instructions@comma{} or that assume all of their arguments
are loop commands.  Consider an example definition of the parser for the @samp{at}
command:

@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
       (@comma{}target-loop
        @comma{}@@(loopy--parse-loop-commands commands))))))
@end lisp

All it does is process its arguments and wrap the resulting instructions in an
@samp{at} instruction.  By wrapping main-body instructions in an @samp{at} instruction@comma{}
@code{loopy-iter} does not see the main-body code@comma{} and so cannot use it.  Instead@comma{} an
alternative parser must be used:

@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@comma{} so we just temporarily `let'-bind
  ;; `loopy-iter--non-main-body-instructions' while the expanding functions push
  ;; to it@comma{} which we then wrap back in a new instruction and pass up to the
  ;; calling function@comma{} 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@comma{} which thinks that this is a normal loop-command parser.
          `(@comma{}@@loopy-result
            (loopy--at-instructions
             (@comma{}target-loop
              @comma{}@@(thread-last loopy-iter--non-main-body-instructions
                             nreverse
                             (apply #'append))))))))
@end lisp

@vindex loopy-iter--non-main-body-instructions
This @code{loopy-iter}-specific definition separates the main-body instructions out
from the other instructions so that @code{loopy-iter} can operate on their values
directly.  The variable @code{loopy-iter--non-main-body-instructions} is a place
where the macro-expanding parsers used by @code{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
@code{loopy-iter-overwritten-command-parsers}.

@node Finding More Examples
@subsection Finding More Examples

If you would like to see more examples@comma{} consider reading through the source code
of @file{loopy-commands.el}@comma{} 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 @key{RET}}.

@node Locally Overriding Behavior
@section Locally Overriding Behavior

The special macro argument @samp{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@comma{} consider namespace collisions: a library which makes a custom loop
command available via @code{loopy-parsers} might overwrite another definition created
by some other@comma{} 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@comma{} 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 @samp{override} is similar to that of the
special macro argument @samp{with}.  Note@comma{} however@comma{} its arguments are not evaluated@comma{}
but taken literally@comma{} similar to normal macro arguments.  As shown in the below
example@comma{} this is not an issue when used with a wrapping macro@comma{} because any
needed evaluation can occur during the execution of the wrapper.

@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
                     @comma{}(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))))
          @comma{}@@args))

(my-pkg--loopy (each i some-seq)
               (vect j some-array)
               (collect (cons i j)))
@end lisp

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:

@table @asis
@item @code{loopy-parsers}
A @samp{map.el}-compatible structure that maps a symbol to a
parsing function.  Parsing functions must be available during macro expansion.
See the user option @code{loopy-parsers} and see @ref{Custom Commands}.

@lisp
(cl-defun my-set-parser ((_name var val))
  `((loopy--other-vars (@comma{}var nil))
    (loopy--main-body (setq @comma{}var @comma{}val))))

(defmacro my-loopy-wrapper (&rest args)
  (let ((map (map-insert loopy-parsers 'my-set #'my-set-parser)))
    `(loopy (override (loopy-parsers @comma{}map))
            @comma{}@@args)))

;; => 3
(my-loopy-wrapper (my-set i 3)
                  (return i))
@end lisp

Overriding @code{loopy-parsers} in @code{loopy-iter} will also override the effects of
any settings in @code{loopy-iter-overwritten-parsers}.  Currently@comma{} there is no
override for @code{loopy-iter-overwritten-parsers}.  Using the override for
@code{loopy-parsers} achieves the same effect.

@lisp
;; The same example as above@comma{} 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 @comma{}list)
                           (loopy-parsers @comma{}map))
                 @comma{}@@args)))
@end lisp

@item @code{loopy-iter-bare-names}
A list of symbols.  See the user option
@code{loopy-iter-bare-names} and see @ref{The @code{loopy-iter} Macro}.

@lisp
(cl-defun my-set-parser ((_name var val))
  `((loopy--other-vars (@comma{}var nil))
    (loopy--main-body (setq @comma{}var @comma{}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 @comma{}list)
                           (loopy-parsers @comma{}map))
                 @comma{}@@args)))

;; => 3
(my-loopy-iter-wrapper (my-set i 3)
                       (return i))
@end lisp

@item @code{loopy-iter-keywords}
A list of symbols.  See the user option
@code{loopy-iter-keywords} and see @ref{The @code{loopy-iter} Macro}.

@lisp
(defmacro my-loopy-iter-wrapper (&rest args)
  (let ((list (cons 'my-keyword loopy-iter-keywords)))
    `(loopy-iter (override (loopy-iter-keywords @comma{}list))
                 @comma{}@@args)))

;; => (1 2 3)
(my-loopy-iter-wrapper (my-keyword list i '(1 2 3))
                       (collecting i))
@end lisp
@end table

@node Comparing to @code{cl-loop}
@chapter Comparing to @code{cl-loop}

@code{loopy} is a better version of @code{cl-loop}@comma{} judging by the following:

@itemize
@item
It is generally as fast or faster than @code{cl-loop}@comma{} though some optimizations
are more explicit.

@item
Loop commands are always evaluated in order.

@item
@code{loopy} provides a more complete destructuring system@comma{} which can be swapped out
with others.

@lisp
;; No array destructuring in `cl-loop'.
;; => (3 7)
(loopy (list [i j] '([1 2] [3 4]))
       (collect (+ i j)))
@end lisp

@item
@code{loopy} provides more looping constructs out of the box with a (according to
the author) clearer naming scheme than @code{cl-loop}'s @samp{for} clauses.

@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 lisp

@item
Taking influence from Common Lisp's Iterate@comma{} several @code{loopy} commands are more
featureful than their @code{cl-loop} counterparts.

@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 lisp

@item
@code{loopy} provides more control over what happens after and around the loop.
@code{cl-loop} lacks equivalents of the following special macro arguments:
@itemize
@item
@samp{after-do}: There is only a @code{finally do}.
@item
@samp{without}: There is no way to prevent @code{cl-loop} from binding variables.
@item
@samp{finally-protect}: Though niche@comma{} @code{loopy} can wrap only the loop body in
@code{unwind-protect}.
@item
@samp{accum-opt}: Named accumulations are always more slow than implicit
accumulations in @code{cl-loop}@comma{} even when they don't need to be.
@item
@samp{wrap}: Again niche@comma{} but missing from @code{cl-loop}.
@end itemize

@item
@code{loopy} is more extensible.  Loop commands can be added easily.

@item
When using @code{loopy-iter}@comma{} the macro is more flexible in how it can be used.
@end itemize

@node Translating to and from @code{cl-loop}
@chapter Translating to and from @code{cl-loop}

@code{loopy} and @code{cl-loop} use slightly different terminology.  The equivalent of
``for clauses'' are referred to as ``iteration commands'' in @code{loopy}@comma{} as they are
generally used for iterating through sequences.  Meanwhile@comma{} ``iteration clauses''
can be separated into ``iteration commands'' (@samp{list} and @samp{array}) and ``early-exit
commands'' (@samp{while} and @samp{until}).

``Accumulation clauses'' work the same as ``accumulation commands''.

@menu
* For Clauses::
* Iteration Clauses::
* Accumulation Clauses::
* Other Clauses::
@end menu

@node For Clauses
@section For Clauses

As Emacs has many functions that return lists@comma{} there is no need to implement an
exact equivalent for every @samp{for}-clause that @code{cl-loop} has.  Instead@comma{} one can
just iterate through the return value of the appropriate function using the
@samp{list} command.

For the commands operating on hash tables@comma{} see also the generic iteration
command @samp{map-pairs}@comma{} which works generically on hash tables@comma{} association lists
(``alists'')@comma{} property lists (``plists'')@comma{} and vectors.

@multitable {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}
@headitem @code{cl-loop}
@tab @code{loopy}
@item @samp{for VAR from EXPR1 to EXPR2 by EXPR3}
@tab @samp{(numbers VAR :from EXPR1 :to EXPR2 :by EXPR3)}
@item @samp{for VAR in LIST [by FUNCTION]}
@tab @samp{(list VAR LIST :by FUNC)}
@item @samp{for VAR on LIST [by FUNCTION]}
@tab @samp{(cons VAR VAL :by FUNC)}
@item @samp{for VAR in-ref LIST by FUNCTION}
@tab @samp{(list-ref VAR LIST :by FUNC)}
@item @samp{for VAR across ARRAY}
@tab @samp{(array VAR ARRAY)}
@item @samp{for VAR across-ref ARRAY}
@tab @samp{(array-ref VAR ARRAY)}
@item @samp{for VAR being the elements of SEQUENCE}
@tab @samp{(sequence VAR SEQUENCE)}
@item @samp{for VAR being the elements of-ref SEQUENCE}
@tab @samp{(sequence-ref VAR SEQUENCE)}
@item @samp{for VAR being the symbols [of OBARRAY]}
@tab None so far.  Use @code{mapatoms}.
@item @samp{for VAR being the hash-keys of HASH-TABLE}
@tab @samp{(list VAR (hash-table-keys HASH-TABLE))}
@item @samp{for VAR being the hash-values of HASH-TABLE}
@tab @samp{(list VAR (hash-table-values HASH-TABLE))}
@item @samp{for VAR being the key-codes of KEYMAP}
@tab None so far.  Use @code{map-keymap}.
@item @samp{for VAR being the key-bindings of KEYMAP}
@tab None so far.  Use @code{map-keymap}.
@item @samp{for VAR being the key-seqs of KEYMAP}
@tab None so far.
@item @samp{for VAR being the overlays [of BUFFER]}
@tab @samp{(list VAR (with-current-buffer BUFFER (car (overlay-lists))))}
@item @samp{for VAR being the intervals [of BUFFER]}
@tab None so far.
@item @samp{for VAR being the frames}
@tab @samp{(list VAR (frame-list))}
@item @samp{for VAR being the windows [of FRAME]}
@tab @samp{(list VAR (window-list FRAME))}
@item @samp{for VAR being the buffers}
@tab @samp{(list VAR (buffer-list))}
@item @samp{for VAR = EXPR1 then EXPR2}
@tab @samp{(set VAR EXPR1 EXPR2)}
@end multitable

@node Iteration Clauses
@section Iteration Clauses

@multitable {aaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaa}
@headitem @code{cl-loop}
@tab @code{loopy}
@item @samp{repeat INT}
@tab @samp{(cycle INT)}
@item @samp{while COND}
@tab @samp{(while COND)}
@item @samp{until COND}
@tab @samp{(until COND)}
@item @samp{iter-by iterator}
@tab @samp{(iter ITERATOR)}
@item @samp{never}
@tab @samp{(never COND)}
@item @samp{always}
@tab @samp{(always COND)}
@item @samp{thereis}
@tab @samp{(thereis COND)}
@end multitable

@node Accumulation Clauses
@section Accumulation Clauses

Like with @code{cl-loop}@comma{} in @code{loopy}@comma{} accumulation commands accumulate into the same
variable when no @samp{VAR} is given (by default@comma{} @code{loopy-result}).

@multitable {aaaaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaa}
@headitem @code{cl-loop}
@tab @code{loopy}
@item @samp{append EXPR into VAR}
@tab @samp{(append VAR EXPR)}
@item @samp{collect EXPR into VAR}
@tab @samp{(collect VAR EXPR)}
@item @samp{concat EXPR into VAR}
@tab @samp{(concat VAR EXPR)}
@item @samp{count EXPR into VAR}
@tab @samp{(count VAR EXPR)}
@item @samp{maximize EXPR into VAR}
@tab @samp{(maximize VAR EXPR)}
@item @samp{minimize EXPR into VAR}
@tab @samp{(minimize VAR EXPR)}
@item @samp{nconc EXPR into VAR}
@tab @samp{(nconc VAR EXPR)}
@item @samp{sum EXPR into VAR}
@tab @samp{(sum VAR EXPR)}
@item @samp{vconcat EXPR into VAR}
@tab @samp{(vconcat VAR EXPR)}
@end multitable

@node Other Clauses
@section Other Clauses

In @code{loopy}@comma{} @samp{if}@comma{} @samp{when}@comma{} and @samp{unless} can take multiple loop commands as
arguments@comma{} and operate more like their Lisp counterparts.

This means that @samp{if} is not a synonym for @samp{when}.  Just like the normal Lisp
special form @code{if}@comma{} @samp{(if COND cmd1 cmd2 cmd3)} only runs @samp{cmd1} if @samp{COND}
evaluates to non-nil@comma{} and only runs commands @samp{cmd2} and @samp{cmd3} if @samp{COND}
evaluates to @code{nil}.

@code{loopy} also provides the command @samp{cond}@comma{} which works like the normal Lisp
special form @code{cond}.

@multitable {aaaaaaaaaaaaaaaaaaaaaa} {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}
@headitem @code{cl-loop}
@tab @code{loopy}
@item @samp{with var = value}
@tab @samp{(with (VAR VALUE))} as a macro argument
@item @samp{if COND clause}
@tab @samp{(if COND CMDS)} as a loop command
@item @samp{when COND clause}
@tab @samp{(when COND CMDS)} as a loop command
@item @samp{unless COND clause}
@tab @samp{(unless COND CMDS)} as a loop command
@item @samp{named NAME}
@tab @samp{NAME} or @samp{(named NAME)} as a macro argument
@item @samp{initially [do] EXPRS}
@tab @samp{(before-do EXPRS)} as a macro argument
@item @samp{finally [do] EXPRS}
@tab @samp{(finally-do EXPRS)} as a macro argument
@item @samp{finally return EXPR}
@tab @samp{(finally-return EXPR)} as a macro argument
@item @samp{do EXPRS}
@tab @samp{(do EXPRS)} as a loop command
@item @samp{return EXPR}
@tab @samp{(return EXPR)} as a loop command
@end multitable

@node Index of Concepts
@chapter Index of Concepts

@printindex cp

@node Index of Variables
@chapter Index of Variables

@printindex vr

@node Index of Functions and Macros
@chapter Index of Functions and Macros

@printindex fn

@node Index of Special Macro Arguments
@chapter Index of Special Macro Arguments

@printindex pg

@node Index of Loop Commands
@chapter Index of Loop Commands

@printindex tp

@bye
