diff --git a/CHANGELOG.md b/CHANGELOG.md index 65e4b3bf..822ad367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ This document describes the user-facing changes to Loopy. +## Unreleased + +### Breaking Changes + +- Conflicting starting values for accumulation variables, such as from using + `sum` (value: 0) and `multiply` (value: 1), now signal an error ([#169], + [#203]). In the future, they will signal an error. + + For now, Loopy continues the behavior of using the first found starting value + from the macro arguments. To silence this warning and to avoid this future + error, use the `with` special macro argument to explicitly state a starting + value for the accumulation variable. + +[#169]: https://github.com/okamsn/loopy/issues/169 +[#203]: https://github.com/okamsn/loopy/pull/203 + ## 0.13.0 ### Breaking Changes diff --git a/README.org b/README.org index 16226913..86314246 100644 --- a/README.org +++ b/README.org @@ -30,6 +30,9 @@ please let me know. ----- _Recent breaking changes:_ + - Unreleased: + - Conflicting initialization values for accumulation variables now signal + a warning. In the future, they will signal an error. - Version 0.13.0: - The deprecated =:init= keyword argument has been removed. Use the =with= special macro argument instead. diff --git a/doc/loopy-doc.org b/doc/loopy-doc.org index 95bac5ca..c07ac38d 100644 --- a/doc/loopy-doc.org +++ b/doc/loopy-doc.org @@ -2558,37 +2558,6 @@ Keep in mind that it is an error to modify accumulation variables outside of accumulation commands. This restriction allows accumulations to be much faster. #+end_quote -#+cindex: accumulation initial values -Like with other loop commands, variables created by accumulation commands (such -as ~coll~ in the above example) are initialized to ~nil~ unless stated -otherwise. When otherwise, such as for the commands =sum= and =multiply=, the -initial value of a variable depends on the first accumulation command using that -variable in the arguments given to the macro. Remember that a variable's -initial value can be controlled using the =with= special macro argument. - -#+begin_src emacs-lisp - ;; => 27 - (loopy (numbers i :from 1 :to 3) - (sum my-accum i) ; Starts at 0. - (multiply my-accum i) - (finally-return my-accum)) - - ;; => 21 - (loopy (numbers i :from 1 :to 3) - (multiply my-accum i) ; Starts at 1. - (sum my-accum i) - (finally-return my-accum)) - - ;; Using `with': - ;; - ;; => 87 - (loopy (with (my-accum 10)) - (numbers i :from 1 :to 3) - (sum my-accum i) ; Starts at 0. - (multiply my-accum i) - (finally-return my-accum)) -#+end_src - #+cindex: accumulation destructuring Similar to iteration commands, accumulation commands can also use destructuring. In accumulation commands, the values resulting from destructuring are @@ -2703,11 +2672,46 @@ other hand, =sum= and =multiply= are compatible, since they both act on numbers. ;; Compatible commands: ;; => 27 - (loopy (numbers i :from 1 :to 3) + (loopy (with (loopy-result 0)) + (numbers i :from 1 :to 3) (sum i) (multiply i)) #+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. + +#+attr_texinfo: :tag Warning +#+begin_quote +Currently, a warning is raised when the default initial values of accumulation +commands conflict. In the future, this will be an error. To resolve this +conflict, use the =with= special macro argument, as noted above. +#+end_quote + +#+begin_src emacs-lisp + ;; Raises a warning. Will raise an error in the future. + ;; + ;; => 27 + (loopy (numbers i :from 1 :to 3) + (sum my-accum i) ; Defaults to 0. + (multiply my-accum i) ; Defaults to 1. + (finally-return my-accum)) + + ;; No warning because using `with': + ;; + ;; => 87 + (loopy (with (my-accum 10)) + (numbers i :from 1 :to 3) + (sum my-accum i) ; Default not used. + (multiply my-accum i) ; Default not used. + (finally-return my-accum)) +#+end_src + By default, 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 @@ -2866,7 +2870,7 @@ all described below. #+begin_quote This argument is similar to the =:test= argument used by =cl-lib=, but is closer to the optional =testfn= argument used by =seq= (for example, in - ~seq-contains-p~). This are two important differences: + ~seq-contains-p~). There are two important differences: 1. Tests default to ~equal~, like in other Emacs Lisp libraries, not ~eql~. 2. The first argument is the existing value or sequence and the second argument is the tested value. This is the /opposite/ of the order used by @@ -2888,7 +2892,7 @@ all described below. #+end_src #+cindex: accumulation keyword key -- =key= :: A one-argument function that transforms the _both_ tested value and +- =key= :: A one-argument function that transforms _both_ the tested value and the value from sequence used by the =test= keyword. The keyword =key= is useful to avoid applying a transforming function to the diff --git a/doc/loopy.texi b/doc/loopy.texi index 58461e41..ba8a9411 100644 --- a/doc/loopy.texi +++ b/doc/loopy.texi @@ -705,7 +705,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,orga8850f4 +@float Listing,org60882dd @lisp ;; => (nil 1 2) (loopy (collect coll i) @@ -796,6 +796,7 @@ indirection. (numbers i :from 0 :to 3)) @end lisp +@cindex keyword evaluation Unlike @code{cl-loop} in some cases, in Loopy, the values passed as keyword arguments are evaluated only once. For example, the command @samp{(list i some-list :by (get-function))} evaluates @code{(get-function)} only once. It does not evaluate it @@ -887,7 +888,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,org2f13e54 +@float Listing,orge44397f @lisp ;; => (1 2 3 4) (cl-loop for (i . j) in '((1 . 2) (3 . 4)) @@ -902,7 +903,7 @@ Below are two examples of destructuring in @code{cl-loop} and @code{loopy}. @caption{Destructuring values in a list.} @end float -@float Listing,orgb1a31ca +@float Listing,org9267070 @lisp ;; => (1 2 3 4) (cl-loop for elem in '((1 . 2) (3 . 4)) @@ -928,9 +929,12 @@ arrays (which includes strings and vectors). To destructure lists, use a list, as in @samp{(a b c)}. @item To destructure arrays, use a vector, as in @samp{[a b c]}. +@item +To destructure sequences generically using @samp{seq.el} (mainly via @code{seq-elt} and +@code{seq-drop}), use a vector or a list whose first element is @samp{&seq}, as in +@samp{[&seq a b c]} and @samp{(&seq a b c)}. @end itemize - This sequence of symbols can be shorter than the destructured sequence, @emph{but not longer}. If shorter, the unassigned elements of the destructured sequence are simply ignored. @@ -990,8 +994,7 @@ In more detail, the elements of the destructuring sequence can be: @item A positional variable which will be bound to the corresponding element in the sequence. These variables can themselves be sequences, but must be of the -correct type. Unlike @code{seq-let}, Loopy does not currently have a generic -syntax for sequences. +correct type. @lisp ;; ((1 2 3) (4 5 6)) @@ -1028,9 +1031,9 @@ means to ignore the element at this location. This can be more efficient. @cindex &whole @itemize @item -The symbol @samp{&whole}: If @samp{&whole} is the first element in the sequence, then -the second element of the sequence names a variable that holds the entire -value of what is destructured. +The symbol @samp{&whole}: If @samp{&whole} is the first element in the sequence (or the +second element if @samp{&seq} is the first), then the following element of the +sequence names a variable that holds the entire value of what is destructured. This is the same as when used in a CL @code{lambda} list. @@ -1060,7 +1063,7 @@ destructured. When used after optional values, the @samp{&rest} value is the subsequence starting at the index after any possible optional values, even when those optional -values are not actually present. If the sequence is not long enough, than the +values are not actually present. If the sequence is not long enough, then the sub-sequence is empty. @lisp @@ -1116,11 +1119,11 @@ This @samp{&rest} is the same as when used in @code{seq-let}. @cindex &optional @itemize @item -The symbol @samp{&optional}: A variable named after @samp{&optional} is optional. If -the list is not long enough to bind the variable, then the variable is bound -to @code{nil} or, if specified, a default value. Additionally, one may bind a -variable to record whether the list was long enough to contain the optional -value. +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, then the variable is bound to @code{nil} or, if specified, a +default value. Additionally, one may bind a variable to record whether the +sequence was long enough to contain the optional value. As in a CL @code{lambda} list, the variable has the one of the following forms: @@ -1182,7 +1185,7 @@ The symbol @samp{&key} or @samp{&keys}: Variables named after @samp{&key} are tr into keys whose values will be sought using @code{plist-get}, which returns @code{nil} if the key isn't found in the list. -Currently, only lists support this destructuring. +Only lists support this destructuring. @lisp ;; => ((1 2 nil) (4 5 nil)) @@ -1282,7 +1285,7 @@ the dot in dotted lists. Like in @samp{cl-lib}, if, after searching for the other keys, there remains an unmatched key in the destructured value, an error is signaled unless @samp{&allow-other-keys} is also used, or unless the key @samp{:allow-other-keys} is -associated with a non-nil value in the property list, even when using @samp{&rest}. +associated with a non-nil value in the property list. @lisp ;; Error due to presence of `:k3': @@ -1302,8 +1305,8 @@ associated with a non-nil value in the property list, even when using @samp{&res @cindex &map @itemize @item -The symbol @samp{&map}: Variables after @samp{&map} are bound similar to @code{map-let} from -the library @samp{map.el}. @samp{&map} works similarly to @samp{&key}, but has a few +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}, but has a few important differences: @enumerate @@ -1341,9 +1344,8 @@ sequence a symbol @samp{VAR} @end itemize -When specifying @samp{KEY}, @samp{VAR} can be a sequence to perform further -destructuring. When @samp{KEY} is not given, then the key is the symbol @samp{VAR}, as -in @code{(quote VAR)}. +When @samp{KEY} is not given, then the key is the symbol @samp{VAR}, as in @code{(quote + VAR)}. Unlike with @samp{&key}, it is not prepended with a colon. @lisp ;; => ((1 2 3 4 27)) @@ -1393,7 +1395,7 @@ values. The use of both is normally redundant. @item The symbol @samp{&aux}: Variables named after @samp{&aux} are bound to the given values. -Like in CL Lib, @samp{&aux} must come last in the list. +Like in CL Lib, @samp{&aux} must come last in the sequence. @lisp ;; => (7 7 7) @@ -1401,6 +1403,25 @@ Like in CL Lib, @samp{&aux} must come last in the list. (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}, then the +sequence will be destructured as a generic sequence using the generic-sequence +library @samp{seq.el}. Specifically, 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, when applicable. + +Sequences destructured using @samp{&seq} can still use @samp{&whole}, @samp{&optional}, +@samp{&rest}, and @samp{&map}. However, lists destructured using @samp{&seq} cannot be +destructured using @samp{&key}. + +@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 @@ -1503,9 +1524,11 @@ argument @samp{without} (@ref{Special Macro Arguments}). @item @samp{(set-prev|prev-expr VAR VAL &key back)} Bind @samp{VAR} to a value @samp{VAL} from a previous cycle in the loop. With @samp{BACK} (default: 1), use the value from that -many cycles previous. This command @emph{does not} work like a queue; it always -uses the value from the @samp{BACK}-th previous cycle, regardless of when the -command is run. +many cycles previous. If not enough cycles have passed yet, 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, +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}, @samp{prev-set}, and @samp{prev}. @@ -1516,10 +1539,13 @@ This command also has the aliases @samp{setting-prev}, @samp{prev-set}, and @sam (collect j)) ;; => (nil nil nil 1 2) -(loopy (list i '(1 2 3 4 5)) - (set-prev j i :back 3) +(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))) @@ -1535,6 +1561,16 @@ This command also has the aliases @samp{setting-prev}, @samp{prev-set}, and @sam (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 @@ -2277,6 +2313,101 @@ resulting sequence of distributed elements. @end lisp @end table +@findex stream +@findex 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), then move to the next n-th element +during each iteration. This command is a special case of the @samp{substream} +command (described below), setting @samp{VAR} to the first element of each +substream. For more information, 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 + +@findex substream +@findex substreaming +@table @asis +@item @samp{(substream VAR EXPR &key by length)} +Iterate through the sub-streams of +stream @samp{EXPR}, similar to the command @samp{cons}. If @samp{by} is non-nil (default: +1), then move to the next n-th substream during each iteration. If @samp{length} +is given, 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}, 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, such as +@code{seq-elt} and @code{seq-do}. + +For efficiency, when possible, @samp{VAR} is initialized to the value of @samp{EXPR}, +not @code{nil}, and is updated at the end of each step in the loop. This is not +possible when destructuring. Such initialization can be overridden by using +the @samp{with} special macro argument, which can result in slower code. + +Sub-streams can only be destructured using the @samp{&seq} feature of the default +destructuring method (@ref{Basic Destructuring}), or by using the @samp{seq} flag +(@ref{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, + ;; 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 @@ -2631,37 +2762,6 @@ accumulation commands. This restriction allows accumulations to be much faster. @end quotation -@cindex accumulation initial values -Like with other loop commands, variables created by accumulation commands (such -as @code{coll} in the above example) are initialized to @code{nil} unless stated -otherwise. When otherwise, such as for the commands @samp{sum} and @samp{multiply}, the -initial value of a variable depends on the first accumulation command using that -variable in the arguments given to the macro. Remember that a variable's -initial value can be controlled using the @samp{with} special macro argument. - -@lisp -;; => 27 -(loopy (numbers i :from 1 :to 3) - (sum my-accum i) ; Starts at 0. - (multiply my-accum i) - (finally-return my-accum)) - -;; => 21 -(loopy (numbers i :from 1 :to 3) - (multiply my-accum i) ; Starts at 1. - (sum my-accum i) - (finally-return my-accum)) - -;; Using `with': -;; -;; => 87 -(loopy (with (my-accum 10)) - (numbers i :from 1 :to 3) - (sum my-accum i) ; Starts at 0. - (multiply my-accum i) - (finally-return my-accum)) -@end lisp - @cindex accumulation destructuring Similar to iteration commands, accumulation commands can also use destructuring. In accumulation commands, the values resulting from destructuring are @@ -2776,11 +2876,46 @@ other hand, @samp{sum} and @samp{multiply} are compatible, since they both act o ;; Compatible commands: ;; => 27 -(loopy (numbers i :from 1 :to 3) +(loopy (with (loopy-result 0)) + (numbers i :from 1 :to 3) (sum i) (multiply i)) @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. + +@quotation Warning +Currently, a warning is raised when the default initial values of accumulation +commands conflict. In the future, this will be an error. To resolve this +conflict, use the @samp{with} special macro argument, as noted above. + +@end quotation + +@lisp +;; Raises a warning. Will raise an error in the future. +;; +;; => 27 +(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 + By default, 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 @@ -2954,7 +3089,7 @@ accumulating sequence. If so, the function should return a non-nil value. @quotation Note This argument is similar to the @samp{:test} argument used by @samp{cl-lib}, but is closer to the optional @samp{testfn} argument used by @samp{seq} (for example, in -@code{seq-contains-p}). This are two important differences: +@code{seq-contains-p}). There are two important differences: @enumerate @item Tests default to @code{equal}, like in other Emacs Lisp libraries, not @code{eql}. @@ -2984,7 +3119,7 @@ argument is the tested value. This is the @emph{opposite} of the order used by @cindex accumulation keyword key @table @asis @item @samp{key} -A one-argument function that transforms the both tested value and +A one-argument function that transforms both the tested value and the value from sequence used by the @samp{test} keyword. The keyword @samp{key} is useful to avoid applying a transforming function to the @@ -4547,7 +4682,7 @@ using the @code{let*} special form. This method recognizes all commands and their aliases in the user option @code{loopy-aliases}. -@float Listing,org5bba81f +@float Listing,org0f10623 @lisp ;; => ((1 2 3) (-3 -2 -1) (0)) (loopy-iter (arg accum-opt positives negatives other) diff --git a/loopy-commands.el b/loopy-commands.el index 8d0a5cdc..6b720eb4 100644 --- a/loopy-commands.el +++ b/loopy-commands.el @@ -1479,7 +1479,7 @@ command. (cl-destructuring-bind (existing-category existing-command) existing-description (unless (eq category existing-category) - (signal 'loopy-incompatible-accumulations + (signal 'loopy-incompatible-accumulation-types (list existing-command command)))) (push (cons key (list category command)) diff --git a/loopy-misc.el b/loopy-misc.el index d7203f45..7a907c52 100644 --- a/loopy-misc.el +++ b/loopy-misc.el @@ -71,12 +71,16 @@ '(loopy-error loopy-bad-command-arguments)) ;;;;; Errors on Accumulations -(define-error 'loopy-incompatible-accumulations - "Loopy: Incompatible accumulations" +(define-error 'loopy-incompatible-accumulation-types + "Loopy: Incompatible accumulation types" + 'loopy-error) + +(define-error 'loopy-incompatible-accumulation-initializations + "Loopy: Incompatible initial values for accumulations" 'loopy-error) (define-error 'loopy-incompatible-accumulation-final-updates - "Loopy: Incompatible accumulations" + "Loopy: Incompatible expectations for accumulations" 'loopy-error) (define-error 'loopy-missing-accum-counters diff --git a/loopy.el b/loopy.el index 7655694e..ca57a159 100644 --- a/loopy.el +++ b/loopy.el @@ -648,9 +648,24 @@ macro `loopy' itself." (loopy--accumulation-vars (loopy--validate-binding instruction-value) - ;; Don't want to accidentally rebind variables to `nil'. - (unless (loopy--bound-p (cl-first instruction-value)) - (push instruction-value loopy--accumulation-vars))) + ;; Don't want to accidentally rebind variables to `nil' + ;; or to accidentally mis-use commands that need + ;; different initial values. + (loopy--pcase-let-workaround (var new-val) + (pcase-let ((`(,var ,new-val) instruction-value)) + (pcase var + ((pred loopy--with-bound-p) nil) + ((and (app loopy--command-bound-p `(,_place . ,old-val)) + (guard (not (equal new-val old-val)))) + ;; TODO: Switch from raising a warning to raising an error. + ;; (signal 'loopy-incompatible-accumulation-initializations + ;; (list :in place :var var :old old-val :new new-val)) + (display-warning + 'loopy + (format "loopy: Conflicting accumulation starting values: `%s', %s, %s\nThis will be an error in the future. To resolve this error, use `with' to explicitly specify a starting value." + var old-val new-val) + :warning)) + (_ (push instruction-value loopy--accumulation-vars)))))) (loopy--other-vars (loopy--validate-binding instruction-value) diff --git a/tests/tests.el b/tests/tests.el index 4369833a..94cfdab4 100644 --- a/tests/tests.el +++ b/tests/tests.el @@ -629,7 +629,7 @@ SYMS-STR are the string names of symbols from `loopy-iter-bare-commands'." :iter-keyword (array loopy)) (loopy-deftest at-disagreeing-accum-types - :error loopy-incompatible-accumulations + :error loopy-incompatible-accumulation-types :macroexpand t :multi-body t :body ((outer @@ -3137,9 +3137,38 @@ expansion time." (nunion . nunioning) (nconc . nconcing))) +;; TODO: Enable these tests in a future version. +;; +;; (loopy-deftest accumulation-compatibility-different-inits-1 +;; :doc "Check that accumulation commands with different initial values raise an error. +;; This should apply even when they're compatible types." +;; :error loopy-incompatible-accumulation-initializations +;; :macroexpand t +;; :body ((list i '(1 2 3 4 5)) +;; (sum i) +;; (multiply i)) +;; :loopy t +;; :iter-keyword (list sum multiply) +;; :iter-bare ((list . listing) +;; (sum . summing) +;; (multiply . multiplying))) +;; +;; (loopy-deftest accumulation-compatibility-different-inits-2 +;; :doc "Check that `with' on the variable (see test 1) avoids the error." +;; :result 27 +;; :body ((with (loopy-result 0)) +;; (list i '(1 2 3)) +;; (sum i) +;; (multiply i)) +;; :loopy t +;; :iter-keyword (list sum multiply) +;; :iter-bare ((list . listing) +;; (sum . summing) +;; (multiply . multiplying))) + (loopy-deftest accumulation-compatibility-different-types :doc "Check that commands with different accumulation types should raise error." - :error loopy-incompatible-accumulations + :error loopy-incompatible-accumulation-types :macroexpand t :multi-body t :body [((list i '((1 2) (3 4) (5 6))) @@ -5518,7 +5547,7 @@ Not multiple of 3: 7" (collect . collecting))) (loopy-deftest thereis-always-same-var - :error loopy-incompatible-accumulations + :error loopy-incompatible-accumulation-types :multi-body t :body [((list i '(1 2 3)) (always i) @@ -5534,7 +5563,7 @@ Not multiple of 3: 7" :iter-bare ((list . listing))) (loopy-deftest thereis-never-same-var - :error loopy-incompatible-accumulations + :error loopy-incompatible-accumulation-types :multi-body t :body [((list i '(1 2 3)) (never i)