branch: elpa/loopy
commit 1e76027423c791614c2ddafb18816ccf4317ea09
Author: okamsn <[email protected]>
Commit: GitHub <[email protected]>
Reorganize documentation on accumulation commands and improve CHANGELOG.
(#248)
- In `loopy-doc.org`, try to make the different topics in Accumulation
(before
the sub-sections) read a bit more cleanly and flow better.
- In `CHANGELOG.md`, make it clear what changed about modifying
`loopy-result`
in `finally-do`.
---
CHANGELOG.md | 40 ++++---
doc/loopy-doc.org | 216 ++++++++++++++++---------------------
doc/loopy.texi | 313 +++++++++++++++++++++++-------------------------------
tests/tests.el | 20 ++++
4 files changed, 268 insertions(+), 321 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cc8cdaf9430..51fc91b5c3c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -47,22 +47,25 @@ For Loopy Dash, see <https://github.com/okamsn/loopy-dash>.
([#234], [#240]). The old implementation used the name of the command in the
generated code and was written before aliases.
-- `finally-do` can now modify the implied return value of a loopy by modifying
- the implied accumulation variable, which is by default `loopy-result`
- ([#244]). Previously, one would have needed to use `finally-return` after
- modifying an implied `loopy-result`.
-
- This improves consistency by allowing treating an implied `loopy-result`
- more like other, explicit accumulation variables inside `finally-do`.
- The cost is switching from the use of `prog1` to an `if` expression with two
- helper variables. This change only applies when an implied return value is
- used with `finally-do`. It does not affect the macro expansion when
- `finally-return` is used.
-
- This is technically a breaking change for those who were modifying an implied
- `loopy-result` but did not wish those changes to be captured in the macro's
- implied return value nor used in `finally-return` (perhaps changing the value
- for side effects only).
+- The macro by default now uses the value of `loopy-result` as the implied
+ return value when `loopy-result` is used as an implied accumulation variable
+ and `finally-do` is used ([#244]). Previously, the macro would store the
+ implied return value in `loopy-result` when `loopy-result` was used as an
+ implied accumulation variable, but any further changes made to `loopy-result`
+ after the loop completed in the `finally-do` special macro argument
+ would not be included in the macro's ultimate return value.
+
+ This was inconsistent with modifying `loopy-result` in the `after-do` special
+ macro argument, in which case the modification /was/ included in the implied
+ return value.
+
+ The new behavior should be less confusing in the event that a user does
modify
+ `loopy-result` in `finally-do`. Previously, one would have needed to use
+ `finally-return` after modifying an implied `loopy-result`. The cost is
+ switching from the use of `prog1` to an `if` expression with two helper
+ variables. This change only applies when an implied return value is used
with
+ `finally-do`. It does not affect the macro expansion when `finally-return`
is
+ used.
```emacs-lisp
;; Previously required way:
@@ -79,6 +82,11 @@ For Loopy Dash, see <https://github.com/okamsn/loopy-dash>.
(finally-do (push 0 loopy-result)))
```
+ This is technically a breaking change for those who were modifying an implied
+ `loopy-result` but did not wish those changes to be captured in the macro's
+ implied return value nor used in `finally-return` (perhaps changing the value
+ for side effects only).
+
- `loopy-default-flags` is now deprecated ([#245]). This prevents one library
from breaking the macro expansions in another library. Instead of using
`loopy-default-flags`, one should use a wrapping macro like the one below
diff --git a/doc/loopy-doc.org b/doc/loopy-doc.org
index 8f893b99ba6..b01e1a2d6a1 100644
--- a/doc/loopy-doc.org
+++ b/doc/loopy-doc.org
@@ -64,7 +64,7 @@ libraries =seq= ([[info:elisp#Sequence Functions]]) and
=cl-lib= ([[info:cl]])).
- [[#sequence-index-iteration][Sequence Index Iteration]]
- [[#sequence-reference-iteration][Sequence Reference Iteration]]
- [[#accumulation][Accumulation]]
- - [[#common-properties-of-accumulation-commands][Common Properties of
Accumulation Commands]]
+ - [[#common-properties-and-arguments-of-accumulation-commands][Common
Properties and Arguments of Accumulation Commands]]
- [[#generic-accumulation][Generic Accumulation]]
- [[#numeric-accumulation][Numeric Accumulation]]
- [[#sequence-accumulation][Sequence Accumulation]]
@@ -2827,10 +2827,12 @@ accumulated, instead of the destructured value.
#+cindex: implied accumulation results
Like in ~cl-loop~, you do not need to supply a variable name to accumulation
-commands. If no accumulation variable is given, then the accumulated value is
-understood to be the return value of the loop. These implied return values can
-be overridden by using the the =return= and =return-from= loop commands or the
-=finally-return= macro argument.
+commands. If no accumulation variable is given and the =finally-do= special
+macro argument is not used, then the accumulated value is understood to be the
+return value of the loop. Such a return value is described in this document as
+an {{{dfn(implied return value)}}} or an {{{dfn(implicit return value)}}}. An
+implied return value can be overridden by using the the =return= and
+=return-from= loop commands or the =finally-return= macro argument.
#+begin_src emacs-lisp
;; => (1 2 3)
@@ -2844,7 +2846,9 @@ be overridden by using the the =return= and =return-from=
loop commands or the
#+vindex: loopy-result
Unlike ~cl-loop~, Loopy uses a default accumulation variable, which is named
-~loopy-result~. This variable can be used in the =after-do=, =finally-do=, and
+~loopy-result~. When used this way, ~loopy-result~ is described as an
+{{{dfn(implied accumulation variable)}}} or an {{{dfn(implicit accumulation
+variable)}}}. This variable can be used in the =after-do=, =finally-do=, and
=finally-return= special macro arguments.
#+begin_src emacs-lisp
@@ -2854,25 +2858,83 @@ Unlike ~cl-loop~, Loopy uses a default accumulation
variable, which is named
(collect i)
(finally-return (cons 0 loopy-result)))
+ ;; => (0 1 2 3)
+ (loopy (list i '(1 2 3))
+ (collect i)
+ (after-do (push 0 loopy-result)))
+
;; => (0 1 2 3)
(loopy (list i '(1 2 3))
(collect i)
(finally-do (push 0 loopy-result)))
#+end_src
-In general, you should not attempt to modify or use the value of ~loopy-result~
-during the loop when it is used as the implied accumulation variable for a
-command, as it is not guaranteed to have a correct value when efficiently
-building sequences. For example, it is often faster to build a list in reverse
-instead of appending to its end. For some commands, such as those in
-[[#accum-numeric]] and [[#accum-generic]], this does not matter.
+Like in ~cl-loop~, when multiple accumulation commands do not name an
+accumulation variable, they will all use the same implied accumulation variable
+(~loopy-result~). As a consequence, one must explicitly name separate
+accumulation variables to be able to accumulate into separate values. This can
+make accumulation slower, because ~loopy~ ensures that named accumulation
+variables (excluding the previously mentioned ~loopy-result~) have the correct
+value during the loop. For example, ~loopy~ will construct named accumulation
+variables containing lists in the correct order, instead of using the more
+efficient ~push~-~nreverse~ idiom. This behavior can be disabled by optimizing
+accumulation variables using the =accum-opt= special macro argument
+([[#optimized-accums]]).
+
+#+begin_src emacs-lisp
+ ;; Both command usages of `collect' and `append'
+ ;; use `loopy-result' as the implied accumulation variable:
+ ;;
+ ;; => (1 11 2 12 3 13)
+ (loopy (list i '(1 2 3))
+ (collect i)
+ (append (list (+ 10 i)) :at end)
+ (finally-return loopy-result))
+#+end_src
+
+In general, you should not attempt to modify or use the value of optimized
+accumulation variables during the loop, including ~loopy-result~ when it is
used
+as the implied accumulation variable for a command, because they are not
+guaranteed to have a correct value when efficiently building sequences
+([[#optimized-accums]]). For some commands, such as those listed in
[[#accum-numeric]]
+and [[#accum-generic]], this does not matter.
+
+#+begin_src emacs-lisp
+ ;; See that `loopy-result' is not in the correct order during the loop
+ ;; (the exact value of an optimized accumulation variable during the loop
+ ;; is an implementation detail):
+ ;;
+ ;; => (6 5 4 3 2 1)
+ (loopy (number i :from 1 :to 10)
+ (collect i)
+ (when (> i 5)
+ (return loopy-result)))
+#+end_src
+
+Implied results are only required to be correct after the loop ends (before
code
+in =else-do= is run). Furthermore, because using a =return= or =return-from=
+command overrides implied return values, using these commands can prevent
+implied accumulation results from being finalized. Using the =leave= command,
+which exits the loop without returning a value, does not affect the correctness
+of implied results.
+
+#+begin_src emacs-lisp
+ ;; See that the `leave' command does not prevent finalizing
+ ;; optimized accumulation variables, unlike the above `return' command:
+ ;;
+ ;; => (1 2 3 4 5 6)
+ (loopy (number i :from 1 :to 10)
+ (collect i)
+ (when (> i 5) (leave)))
+#+end_src
Be aware that explicitly named accumulation variables do not affect the implied
return value of a loop. Such values must be returned explicitly, or they will
be ignored when the macro returns a value. This limitation is needed for more
consistently handling the complexity that comes from allowing unknown kinds of
-destructuring via the alternative destructuring systems. This may change in
the
-future.
+destructuring via the alternative destructuring systems, such as the possibly
+intermediate variables that can be generated by ~pcase~ patterns. This may
+change in the future.
#+begin_src emacs-lisp
;; See how the variable `my-explicit-variable' is ignored when
@@ -2905,14 +2967,12 @@ accumulation results.
#+end_src
#+cindex: accumulation compatibility
-Like in ~cl-loop~, when using implied variables, multiple accumulation commands
-will use the same variable (~loopy-result~). For _all_ accumulation variables
-used by multiple accumulation commands, you should make sure that the commands
-are actually compatible. If not, then ~loopy~ will signal an error.
-
-For example, you should not try to accumulate =collect= results and =sum=
-results into the same variable, as you cannot use a list as a number. On the
-other hand, =sum= and =multiply= are compatible, since they both act on
numbers.
+For all accumulation variables used by multiple accumulation commands, you
+should make sure that the commands are actually compatible. If not, then
+~loopy~ will signal an error. For example, you should not try to accumulate
+=collect= results and =sum= results into the same variable, as you cannot use a
+list as a number. On the other hand, =sum= and =multiply= are compatible,
since
+they both act on numbers.
#+begin_src emacs-lisp
;; Incompatible commands:
@@ -2930,12 +2990,13 @@ other hand, =sum= and =multiply= are compatible, since
they both act on numbers.
#+end_src
#+cindex: accumulation initial values
-Each accumulation command has a default initialization value for the
-accumulation variable. For most commands, this is ~nil~. This documentation
-tries to note when it is not ~nil~. For example, the default starting value
for
-the =sum= command is ~0~ and the default starting value for the =multiply=
-command is ~1~. The default initialization value used by an accumulation
-command can be overridden using the =with= special macro argument.
+In the same general area as accumulation command compatability, you should keep
+in mind that each accumulation command has a default initialization value for
+the accumulation variable. For most commands, this is ~nil~. This
+documentation tries to note when it is not ~nil~. For example, the default
+starting value for the =sum= command is ~0~ and the default starting value for
+the =multiply= command is ~1~. The default initialization value used by an
+accumulation command can be overridden using the =with= special macro argument.
#+attr_texinfo: :tag Warning
#+begin_quote
@@ -2963,104 +3024,7 @@ conflict, use the =with= special macro argument, as
noted above.
(finally-return my-accum))
#+end_src
-One must specify separate accumulation variables to be able to accumulate into
-separate values. This can make accumulation slower, because ~loopy~ ensures
-that named accumulation variables (excluding the previously mentioned
-~loopy-result~) have the correct value during the loop. For example, ~loopy~
-will construct named accumulation variables containing lists in the correct
-order, instead of using the more efficient ~push~-~nreverse~ idiom. This
-behavior can be disabled by optimizing accumulations using the =accum-opt=
-special macro argument ([[#optimized-accums]]).
-
-Below are examples of an optimized accumulation and an un-optimized
-accumulation. See that the example expansion of the un-optimized accumulation
-is more complex and uses a slower way of building the accumulated list.
-
-#+begin_src emacs-lisp
- ;; Optimized accumulation:
- ;;
- ;; => (1 3 2 6 3 9)
- (loopy (accum-opt coll)
- (numbers i :from 1 :to 3)
- (collect coll i)
- (collect coll (* i 3))
- (finally-return coll))
-
- ;; Optimized example expansion:
- ;;
- ;; => (1 3 2 6 3 9)
- (let* ((coll nil)
- (i 1)
- (nums-end192 3)
- (nums-increment191 1))
- (cl-block nil
- (while (<= i nums-end192)
- (setq coll (cons i coll))
- (setq coll (cons (* i 3) coll))
- (setq i (1+ i)))
- (setq coll (nreverse coll)))
- coll)
-#+end_src
-
-#+begin_src emacs-lisp
- ;; Unoptimized accumulation:
- ;;
- ;; => (1 3 2 6 3 9)
- (loopy (numbers i :from 1 :to 3)
- (collect coll i)
- (collect coll (* i 3))
- (finally-return coll))
-
- ;; Unoptimized example expansion:
- ;;
- ;; => (1 3 2 6 3 9)
- (let* ((coll nil)
- (coll-last-link-190 coll)
- (i 1)
- (nums-end189 3)
- (nums-increment188 1))
- (cl-block nil
- (while (<= i nums-end189)
- (cond
- (coll-last-link-190
- (setcdr coll-last-link-190 (list i))
- (setq coll-last-link-190 (cdr coll-last-link-190)))
- (coll
- (setq coll-last-link-190 (last coll))
- (setcdr coll-last-link-190 (list i))
- (setq coll-last-link-190 (cdr coll-last-link-190)))
- (t
- (setq coll (list i)
- coll-last-link-190 coll)))
- (cond
- (coll-last-link-190
- (setcdr coll-last-link-190 (list (* i 3)))
- (setq coll-last-link-190 (cdr coll-last-link-190)))
- (coll
- (setq coll-last-link-190 (last coll))
- (setcdr coll-last-link-190 (list (* i 3)))
- (setq coll-last-link-190 (cdr coll-last-link-190)))
- (t
- (setq coll (list (* i 3))
- coll-last-link-190 coll)))
- (setq i (1+ i))))
- coll)
-#+end_src
-
-#+attr_texinfo: :tag Warning
-#+begin_quote
-In general, you should not try to access implied (or optimized) accumulation
-results (for example, ~loopy-result~) while the loop is running. Implied
-results are only required to be correct after the loop ends (before code in
-=else-do= is run), allowing for more efficient code.
-
-Furthermore, because using a =return= or =return-from= command overrides
implied
-return values, using these commands can prevent implied accumulation results
-from being finalized. Using the =leave= command, which exits the loop without
-returning a value, does not affect the correctness of implied results.
-#+end_quote
-
-*** Common Properties of Accumulation Commands
+*** Common Properties and Arguments of Accumulation Commands
:PROPERTIES:
:END:
diff --git a/doc/loopy.texi b/doc/loopy.texi
index 7a0b4520529..4ed390af518 100644
--- a/doc/loopy.texi
+++ b/doc/loopy.texi
@@ -73,7 +73,7 @@ Iteration
Accumulation
-* Common Properties of Accumulation Commands::
+* Common Properties and Arguments of Accumulation Commands::
* Generic Accumulation:: Accumulating function output.
* Numeric Accumulation:: Using and returning numbers.
* Sequence Accumulation:: Using and returning sequences.
@@ -746,7 +746,7 @@ You should keep in mind that commands are evaluated in
order. This means that
attempting something like the below example might not do what you expect, as
@samp{i}
is assigned a value from the list after collecting @samp{i} into @samp{coll}.
-@float Listing,org05823f8
+@float Listing,org572b5bd
@lisp
;; => (nil 1 2)
(loopy (collect coll i)
@@ -935,7 +935,7 @@ 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,org02ede57
+@float Listing,org99c53b0
@lisp
;; => (1 2 3 4)
(cl-loop for (i . j) in '((1 . 2) (3 . 4))
@@ -950,7 +950,7 @@ Below are two examples of destructuring in @code{cl-loop}
and @code{loopy}.
@caption{Destructuring values in a list.}
@end float
-@float Listing,org9a7343f
+@float Listing,org7212adc
@lisp
;; => (1 2 3 4)
(cl-loop for elem in '((1 . 2) (3 . 4))
@@ -3037,10 +3037,12 @@ accumulated, instead of the destructured value.
@cindex implied accumulation results
Like in @code{cl-loop}, you do not need to supply a variable name to
accumulation
-commands. If no accumulation variable is given, then the accumulated value is
-understood to be the return value of the loop. These implied return values can
-be overridden by using the the @samp{return} and @samp{return-from} loop
commands or the
-@samp{finally-return} macro argument.
+commands. If no accumulation variable is given and the @samp{finally-do}
special
+macro argument is not used, then the accumulated value is understood to be the
+return value of the loop. Such a return value is described in this document as
+an @dfn{implied return value} or an @dfn{implicit return value}. An
+implied return value can be overridden by using the the @samp{return} and
+@samp{return-from} loop commands or the @samp{finally-return} macro argument.
@lisp
;; => (1 2 3)
@@ -3054,7 +3056,8 @@ be overridden by using the the @samp{return} and
@samp{return-from} loop command
@vindex loopy-result
Unlike @code{cl-loop}, Loopy uses a default accumulation variable, which is
named
-@code{loopy-result}. This variable can be used in the @samp{after-do},
@samp{finally-do}, and
+@code{loopy-result}. When used this way, @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},
@samp{finally-do}, and
@samp{finally-return} special macro arguments.
@lisp
@@ -3064,25 +3067,83 @@ Unlike @code{cl-loop}, Loopy uses a default
accumulation variable, which is name
(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
-In general, you should not attempt to modify or use the value of
@code{loopy-result}
-during the loop when it is used as the implied accumulation variable for a
-command, as it is not guaranteed to have a correct value when efficiently
-building sequences. For example, it is often faster to build a list in reverse
-instead of appending to its end. For some commands, such as those in
-@ref{Numeric Accumulation} and @ref{Generic Accumulation}, this does not
matter.
+Like in @code{cl-loop}, when multiple accumulation commands do not name an
+accumulation variable, they will all use the same implied accumulation variable
+(@code{loopy-result}). As a consequence, one must explicitly name separate
+accumulation variables to be able to accumulate into separate values. This can
+make accumulation slower, because @code{loopy} ensures that named accumulation
+variables (excluding the previously mentioned @code{loopy-result}) have the
correct
+value during the loop. For example, @code{loopy} will construct named
accumulation
+variables containing lists in the correct order, 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, you should not attempt to modify or use the value of optimized
+accumulation variables during the loop, including @code{loopy-result} when it
is used
+as the implied accumulation variable for a command, because they are not
+guaranteed to have a correct value when efficiently building sequences
+(@ref{Optimizing Accumulations}). For some commands, such as those listed in
@ref{Numeric Accumulation}
+and @ref{Generic Accumulation}, 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, because using a @samp{return} or
@samp{return-from}
+command overrides implied return values, using these commands can prevent
+implied accumulation results from being finalized. Using the @samp{leave}
command,
+which exits the loop without returning a value, does not affect the correctness
+of implied results.
+
+@lisp
+;; See that the `leave' command does not prevent finalizing
+;; optimized accumulation variables, unlike the above `return' command:
+;;
+;; => (1 2 3 4 5 6)
+(loopy (number i :from 1 :to 10)
+ (collect i)
+ (when (> i 5) (leave)))
+@end lisp
Be aware that explicitly named accumulation variables do not affect the implied
return value of a loop. Such values must be returned explicitly, or they will
be ignored when the macro returns a value. This limitation is needed for more
consistently handling the complexity that comes from allowing unknown kinds of
-destructuring via the alternative destructuring systems. This may change in
the
-future.
+destructuring via the alternative destructuring systems, 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
@@ -3115,14 +3176,12 @@ accumulation results.
@end lisp
@cindex accumulation compatibility
-Like in @code{cl-loop}, when using implied variables, multiple accumulation
commands
-will use the same variable (@code{loopy-result}). For all accumulation
variables
-used by multiple accumulation commands, you should make sure that the commands
-are actually compatible. If not, then @code{loopy} will signal an error.
-
-For example, you should not try to accumulate @samp{collect} results and
@samp{sum}
-results into the same variable, as you cannot use a list as a number. On the
-other hand, @samp{sum} and @samp{multiply} are compatible, since they both act
on numbers.
+For all accumulation variables used by multiple accumulation commands, you
+should make sure that the commands are actually compatible. If not, then
+@code{loopy} will signal an error. For example, you should not try to
accumulate
+@samp{collect} results and @samp{sum} results into the same variable, as you
cannot use a
+list as a number. On the other hand, @samp{sum} and @samp{multiply} are
compatible, since
+they both act on numbers.
@lisp
;; Incompatible commands:
@@ -3140,12 +3199,13 @@ other hand, @samp{sum} and @samp{multiply} are
compatible, since they both act o
@end lisp
@cindex accumulation initial values
-Each accumulation command has a default initialization value for the
-accumulation variable. For most commands, this is @code{nil}. This
documentation
-tries to note when it is not @code{nil}. For example, 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.
+In the same general area as accumulation command compatability, you should keep
+in mind that each accumulation command has a default initialization value for
+the accumulation variable. For most commands, this is @code{nil}. This
+documentation tries to note when it is not @code{nil}. For example, 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.
@quotation Warning
Currently, a warning is raised when the default initial values of accumulation
@@ -3173,105 +3233,8 @@ conflict, use the @samp{with} special macro argument,
as noted above.
(finally-return my-accum))
@end lisp
-One must specify separate accumulation variables to be able to accumulate into
-separate values. This can make accumulation slower, because @code{loopy}
ensures
-that named accumulation variables (excluding the previously mentioned
-@code{loopy-result}) have the correct value during the loop. For example,
@code{loopy}
-will construct named accumulation variables containing lists in the correct
-order, instead of using the more efficient @code{push}-@code{nreverse} idiom.
This
-behavior can be disabled by optimizing accumulations using the @samp{accum-opt}
-special macro argument (@ref{Optimizing Accumulations}).
-
-Below are examples of an optimized accumulation and an un-optimized
-accumulation. See that the example expansion of the un-optimized accumulation
-is more complex and uses a slower way of building the accumulated list.
-
-@lisp
-;; Optimized accumulation:
-;;
-;; => (1 3 2 6 3 9)
-(loopy (accum-opt coll)
- (numbers i :from 1 :to 3)
- (collect coll i)
- (collect coll (* i 3))
- (finally-return coll))
-
-;; Optimized example expansion:
-;;
-;; => (1 3 2 6 3 9)
-(let* ((coll nil)
- (i 1)
- (nums-end192 3)
- (nums-increment191 1))
- (cl-block nil
- (while (<= i nums-end192)
- (setq coll (cons i coll))
- (setq coll (cons (* i 3) coll))
- (setq i (1+ i)))
- (setq coll (nreverse coll)))
- coll)
-@end lisp
-
-@lisp
-;; Unoptimized accumulation:
-;;
-;; => (1 3 2 6 3 9)
-(loopy (numbers i :from 1 :to 3)
- (collect coll i)
- (collect coll (* i 3))
- (finally-return coll))
-
-;; Unoptimized example expansion:
-;;
-;; => (1 3 2 6 3 9)
-(let* ((coll nil)
- (coll-last-link-190 coll)
- (i 1)
- (nums-end189 3)
- (nums-increment188 1))
- (cl-block nil
- (while (<= i nums-end189)
- (cond
- (coll-last-link-190
- (setcdr coll-last-link-190 (list i))
- (setq coll-last-link-190 (cdr coll-last-link-190)))
- (coll
- (setq coll-last-link-190 (last coll))
- (setcdr coll-last-link-190 (list i))
- (setq coll-last-link-190 (cdr coll-last-link-190)))
- (t
- (setq coll (list i)
- coll-last-link-190 coll)))
- (cond
- (coll-last-link-190
- (setcdr coll-last-link-190 (list (* i 3)))
- (setq coll-last-link-190 (cdr coll-last-link-190)))
- (coll
- (setq coll-last-link-190 (last coll))
- (setcdr coll-last-link-190 (list (* i 3)))
- (setq coll-last-link-190 (cdr coll-last-link-190)))
- (t
- (setq coll (list (* i 3))
- coll-last-link-190 coll)))
- (setq i (1+ i))))
- coll)
-@end lisp
-
-@quotation Warning
-In general, you should not try to access implied (or optimized) accumulation
-results (for example, @code{loopy-result}) while the loop is running. Implied
-results are only required to be correct after the loop ends (before code in
-@samp{else-do} is run), allowing for more efficient code.
-
-Furthermore, because using a @samp{return} or @samp{return-from} command
overrides implied
-return values, using these commands can prevent implied accumulation results
-from being finalized. Using the @samp{leave} command, which exits the loop
without
-returning a value, does not affect the correctness of implied results.
-
-@end quotation
-
@menu
-* Common Properties of Accumulation Commands::
+* Common Properties and Arguments of Accumulation Commands::
* Generic Accumulation:: Accumulating function output.
* Numeric Accumulation:: Using and returning numbers.
* Sequence Accumulation:: Using and returning sequences.
@@ -3279,8 +3242,8 @@ returning a value, does not affect the correctness of
implied results.
* Optimizing Accumulations:: Producing efficient accumulations.
@end menu
-@node Common Properties of Accumulation Commands
-@subsection Common Properties of Accumulation Commands
+@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, instead of
@@ -5007,7 +4970,7 @@ using the @code{let*} special form.
This method recognizes all commands and their aliases in the user option
@code{loopy-parsers}.
-@float Listing,orgf6ce5d7
+@float Listing,org0175e90
@lisp
;; => ((-9 -8 -7 -6 -5 -4 -3 -2 -1)
;; (0)
@@ -5408,11 +5371,6 @@ method).
Flags are applied in order. If you specify @samp{(flags seq pcase)}, then
@code{loopy}
will use @code{pcase-let} for destructuring, not @code{seq-let}.
-@vindex loopy-default-flags
-If you wish to always use a flag, you can add that flag to the list
-@code{loopy-default-flags}. These can be overridden by any flag given in the
@samp{flag}
-special macro argument.
-
The following flags are currently supported:
@cindex pcase flag
@@ -5504,45 +5462,6 @@ yet provide the required functionality.
(finally-return (+ sum1 v1) (+ sum2 v2)))
@end lisp
-If you wish to always use one of the destructuring flags, 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}.
Hence,
-uses of @samp{flag}, including aliases, 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', but default to `pcase' destructuring."
- (loopy (with (flag-found nil))
- (list arg body)
- (collect (if (and (consp arg)
- (eq (map-elt loopy-parsers (car arg))
- (map-elt loopy-parsers 'flag)))
- (progn
- (setq flag-found t)
- (append arg '(pcase)))
- arg))
- (finally-return
- `(loopy ,@@(unless flag-found
- '((flag pcase)))
- ,@@loopy-result))))
-
-;; => (1 2 3 4)
-(my-loopy-flag-wrapper (list `(,i . ,j) '((1 . 2) (3 . 4)))
- (collect i)
- (collect j))
-
-;; Ignores the `seq' flag as expected:
-;;
-;; => ( 1 2 3 4)
-(my-loopy-flag-wrapper (flag seq)
- (list `(,i . ,j) '((1 . 2) (3 . 4)))
- (collect i)
- (collect j))
-@end lisp
-
-
@quotation Warning
For accumulation commands, there is no guarantee that a variable that was used
in destructuring was meant to be user-facing. Destructuring systems can create
@@ -5551,14 +5470,12 @@ variables.
@end quotation
-
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 who create the variable, or the destructuring system. As a
result,
@code{temporary?} is treated as an accumulation variable. Such cases can be
unwanted
and produce inefficient code.
-
@lisp
;; Possibly unexpected behavior:
;;
@@ -5571,6 +5488,44 @@ and produce inefficient code.
(finally-return whole temporary?))
@end lisp
+If you wish to always use one of the destructuring flags, 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, uses of @samp{flag}, including aliases, 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', but default to `pcase' destructuring."
+ (loopy (with (entry-for-flag (map-elt loopy-parsers 'flag)))
+ (list arg body)
+ (if (and (consp arg)
+ (eq (map-elt loopy-parsers (car arg))
+ entry-for-flag))
+ (command-do
+ (set flag-found t)
+ (collect (append arg '(pcase))))
+ (collect arg))
+ (finally-do
+ (unless flag-found
+ (push '(flag pcase) loopy-result))
+ (push 'loopy loopy-result))))
+
+;; => (1 2 3 4)
+(my-loopy-flag-wrapper (list `(,i . ,j) '((1 . 2) (3 . 4)))
+ (collect i)
+ (collect j))
+
+;; Ignores the `seq' flag as expected:
+;;
+;; => ( 1 2 3 4)
+(my-loopy-flag-wrapper (flag seq)
+ (list `(,i . ,j) '((1 . 2) (3 . 4)))
+ (collect i)
+ (collect j))
+@end lisp
+
@node Custom Aliases
@section Custom Aliases
@@ -5597,7 +5552,7 @@ 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,orgc46048a
+@float Listing,org7c76d17
@lisp
;; => ("a" "b" "c" "d")
(loopy (array i "abcd")
diff --git a/tests/tests.el b/tests/tests.el
index 3ebd54107e5..a85ffbc5c81 100644
--- a/tests/tests.el
+++ b/tests/tests.el
@@ -466,6 +466,26 @@ writing a `seq-do' method for the custom seq."
:iter-keyword t
:repeat _after)
+(loopy-deftest after-do-modifies-implied-result-var
+ :doc "`after-do' should be able to modify the return value by modifying
+`loopy-result', even when `loopy-result' is used as an impled return value.
+
+This case should not have to be treated differently by the user."
+ :result '(0 1 2 3)
+ :multi-body t
+ :body [((list i '(1 2 3))
+ (collect i)
+ (after-do (push 0 loopy-result)))
+
+ ((list i '(1 2 3))
+ (collect loopy-result i)
+ (after-do (push 0 loopy-result))
+ (finally-return loopy-result))]
+ :loopy t
+ :iter-keyword (list collect)
+ :iter-bare ((list . listing)
+ (collect . collecting)))
+
;;;; Before and After
(loopy-deftest basic-before-and-after-test
:result 3