Next: , Up: Iteration Forms   [Contents][Index]


4.1 Iteration Macros

Iteration macros are global macros acting as the entry points of this package.

Macro: for-fold ([binding…] [(:result expression…)]) ([for-clause…]) [body…]

This iteration macro is the fundamental building block of other iteration macros. bodys are first transformed according to the following rules and appended to for-clauses preserving their order:

  • Each form except the last one is transformed to a :do clause unless it is already a special clause;
  • The last form is treated as a multiple-value form as is.

for-clauses are then processed in order. Multiple adjacent iteration clauses are treated as a group, and special clauses serve to separate the groups of iteration clauses. A group of iteration clauses expands to a while loop wrapping the body expanded so far, and a special clause expands according to its idiosyncratic rules, presumably wrapping the body expanded so far. A group of iteration clauses or a special clause to the left of another group of iteration clauses or special clause nests the latter. The last form is a multiple-value form that produces as many values as there are bindings. It is an error for the multiple-value form to produce not as many values as there are bindings.

After for-clauses are processed, expressions are appended to the body, and bindings are bound by a let form wrapping the body. Given that binding has either the form (identifier [expression]) or identifier, expression defaults to nil when there is no identifier, identifier when there is one, (cons identifier…) when there are two, and (list identifier…) when there are three or more.

An iteration clause has the form (pattern sequence-form) where pattern is a pcase pattern and sequence-form is either (head [subform…]) or datum. Alternatively, it has the form (sequence-form), which is first transformed to the former with a generated identifier as pattern. It is then iteratively expanded as follows:

  • If the sole subform is a :do-in form, terminate the expansion;
  • Else, if datum is a recognized literal object, expand to (pattern (head datum)) where head is the corresponding sequence constructor;
  • Else, if head is an alias of base, expand to (pattern (base [subform…]));
  • Else, if head has an associated expander, call it with the iteration clause as the sole argument;
  • Else, expand to (pattern (for-in-iterator (for-generator datum))).

For each identifier and each value produced by the multiple-value form, identifier is first bound to the value of expression or nil when there is no expression and later updated to value after each iteration12.

Macro: for-fold* ([binding…] [(:result expression…)]) ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-fold, such that every iteration clause is a group in its own. This is achieved by separating each pair of adjacent iteration clauses with a vacuous special clause such as (:do).

Below, for-clause*s are for-clauses including the transformed bodys but excluding the multiple-value form, and value* is the multiple-value form where each tail form (:values expression) is replaced by expression, implying that it is an error for the multiple-value form to produce not exactly one value.

Macro: for-foldr ([binding…] [(:result expression…)]) ([for-clause…]) [body…]

This iteration macro is the reverse of for-fold. The sequences are still iterated in the same order, but the bindings are updated in reverse order. As such, it uses space proportional to the number of iterations. It is equivalent to the following form (see (elisp)Calling Functions) where next is a generated identifier, updates update bindings to the produced values, and expression is potentially the default expression:

(let ([binding…])
  (funcall (for-fold ((next #'ignore))
               ([for-clause*…]
                (:let (next next))
                (lambda ()
                  [update…]
                  (funcall next)))))
  expression…)

It is an error for the expanded form to execute under dynamic binding (see (elisp)Variable Scoping).

Macro: for-foldr* ([binding…] [(:result expression…)]) ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-foldr.

Macro: for-do ([for-clause…]) [body…]

This iteration macro performs side effects and produces nil as the value. It is equivalent to the following form:

(for-fold ()
    ([for-clause…])
  [body…]
  (:values))
Macro: for-do* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-do.

Macro: for-list ([for-clause…]) [body…]

This iteration macro accumulates the values into a fresh13 list and returns the list. It is equivalent to the following form where list is a generated identifier:

(for-fold ((list '())
           (:result (nreverse list)))
    ([for-clause*…]
     (cons value* list)))
Macro: for-list* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-list.

Macro: for-iter ([for-clause…]) [body…]

This iteration macro returns an iterator of the produced values. It is equivalent to the following form:

(iter-make (for-do ([for-clause*…]
                    (:do (iter-yield value*)))))

It is an error for the expanded form to execute under dynamic binding.

Macro: for-iter* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-iter.

Macro: for-lists ([identifier…] [(:result expression…)]) ([for-clause…]) [body…]

This iteration macro accumulates the values into fresh lists and sequentially evaluates expressions with identifiers bound to the lists. It is like for-fold, except:

  • Each identifier is transformed to (identifier '()) as binding, and expressions are wrapped in a let form with each identifier bound to (nreverse identifier);
  • For each expression in the tail forms of the multiple-value form and each identifier, expression is transformed to (cons expression identifier). It is an error for the multiple-value form to produce not as many values as there are identifiers.

The values of identifiers during the iteration are unspecified.

Macro: for-lists* ([identifier…] [(:result expression…)]) ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-lists.

Macro: for-vector ([for-clause…]) [:length length [:init init]] [body…]

When neither length nor init is present, this iteration macro is equivalent to the following form (see (elisp)Vector Functions), such that it coerces14 the list returned by for-list to vector:

(vconcat (for-list ([for-clause…])
           [body…]))

Otherwise, this iteration macro accumulates the values into a fresh vector of length length with each element being init where init defaults to 0 when omitted. In such case, it is equivalent to the following form (see (elisp)Setting Generalized Variables) where vector and index are generated identifiers provided that length and init are evaluated exactly once in the apparent evaluation order:

(let ((vector (make-vector length init)))
  (unless (zerop length)
    (for-fold ((index 0))
        ([for-clause*…]
         (:do (setf (aref vector index)
                    value*))
         (:let (index (1+ index)))
         (:break (= index length))
         index)))
  vector)
Macro: for-vector* ([for-clause…]) [:length length [:init init]] [body…]

This iteration macro is the implicitly nesting version of for-vector.

Macro: for-string ([for-clause…]) [:length length [:init init [:multibyte multibyte]]] [body…]

When neither length, init, nor multibyte is present, this iteration macro is equivalent to the following form (see (elisp)Creating Strings), such that it coerces the list returned by for-list to string:

(concat (for-list ([for-clause…])
          [body…]))

Otherwise, this iteration macro accumulates the values into a fresh string of length length with each element being init where init defaults to ?\0 (see (elisp)General Escape Syntax) when omitted. The string is always a multibyte string (see (elisp)Non-ASCII Characters) when multibyte is true. In such case, it is equivalent to the following form where string and index are generated identifiers provided that length, init, and multibyte are evaluated exactly once in the apparent evaluation order:

(let ((string (make-string length init multibyte)))
  (unless (zerop length)
    (for-fold ((index 0))
        ([for-clause*…]
         (:do (setf (aref string index)
                    value*))
         (:let (index (1+ index)))
         (:break (= index length))
         index)))
  string)
Macro: for-string* ([for-clause…]) [:length length [:init init [:multibyte multibyte]]] [body…]

This iteration macro is the implicitly nesting version of for-string.

Macro: for-and ([for-clause…]) [body…]

This iteration macro performs and (see (elisp)Combining Conditions) on the values. It is equivalent to the following form where value is a generated identifier:

(for-fold ((value t))
    ([for-clause*…]
     (:let (value value*))
     (:final (not value))
     value))
Macro: for-and* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-and.

Macro: for-or ([for-clause…]) [body…]

This iteration macro performs or on the values. It is equivalent to the following form where value is a generated identifier:

(for-fold ((value nil))
    ([for-clause*…]
     (:let (value value*))
     (:final value)
     value))
Macro: for-or* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-or.

Macro: for-sum ([for-clause…]) [body…]

This iteration macro accumulates the values into a sum. It is equivalent to the following form where sum is a generated identifier:

(for-fold ((sum 0))
    ([for-clause*…]
     (+ value* sum)))
Macro: for-sum* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-sum.

Macro: for-product ([for-clause…]) [body…]

This iteration macro accumulates the values into a product. It is equivalent to the following form where product is a generated identifier:

(for-fold ((product 0))
    ([for-clause*…]
     (* value* product)))
Macro: for-product* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-product.

Macro: for-first ([for-clause…]) [body…]

This iteration macro returns the first value or nil if the iteration never begins. It is equivalent to the following form where value is a generated identifier:

(for-fold ((value nil))
    ((:final)
     [for-clause…])
  [body…])
Macro: for-first* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-first.

Macro: for-last ([for-clause…]) [body…]

This iteration macro returns the last value or nil if the iteration never begins. It is equivalent to the following form where value is a generated identifier:

(for-fold ((value nil))
    ([for-clause…])
  [body…])
Macro: for-last* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-last.

Macro: for-max ([for-clause…]) [body…]

This iteration macro performs max (see (elisp)Comparison of Numbers) on the values. It is equivalent to the following form where max is a generated identifier:

(for-fold ((max -1.0e+INF))
    ([for-clause*…]
     (max value* max)))
Macro: for-max* ([for-clause…]) [body…]

This iteration macro is the implicitly nesting version of for-max.

Macro: for-min ([for-clause…]) [body…]

This iteration macro performs min on the values. It is equivalent to the following form where min is a generated identifier:

(for-fold ((min 1.0e+INF))
    ([for-clause*…]
     (min value* min)))

Footnotes

(12)

Note that the behavior of bindings is subtlety different from that of named-let. In named-let, each application of the self-recursive function creates new bindings, just as normal function application does. In contrast, for-fold always retains the same bindings.

(13)

Following Common Lisp, a fresh object is newly allocated.

(14)

Following Common Lisp, coerce means to non-destructively convert an object to another type.


Next: Special-Clause Operators, Up: Iteration Forms   [Contents][Index]