You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This affects:
- `cons`
- `cycle`
- `iter`
- `numbers`
- `seq-index`
Remove optimizations that use iterations variables more directly by initializing
them to the value expected during the first iteration step. Doing that can
change behavior, as iteration variables could be updated again before the loop
is expected to exit, which currently happens for `cl-loop`.
```emacs-lisp
;; => (4 (1 2 3))
(cl-loop for elem in (list 1 2 3)
for i from 1
collect i into is
finally return (list i is))
```
Loopy currently copies that behavior, which could introduce unexpected results
by default. For example, SBCL gives a different result.
```commonlisp
;; => (3 (1 2 3))
(loop for elem in (list 1 2 3)
for num from 1
collect num into nums
finally (return (list num nums)))
```
Making this optimization optional (as we currently do via use of the `with`
special macro argument) means that users would have to remember the internal
implementations of each command with optimization to remember how they change.
See also #204.
If one were to add an `iter-opt` special macro argument and change default
behavior to be expected (more like SBCL), that would only move the burden to
needing to remember when to use `iter-opt`.
As mention in #204, using `iter-opt` requires the user to:
1. Read the documentation to know that a command supports it
2. Always remember that the command supports it while using the macro
3. Always remember how it affects the command while using it
all for a slight gain in speed and marginally less typing.
For those reasons, instead of adding `iter-opt`, it is better to remove these
few optimized behaviors in favor of more standard behavior. As noted in #204,
instead of writing
```emacs-lisp
;; => (5 (1 2 3 4))
(loopy (iter-opt (numbers i))
(list elem '(1 2 3 4))
(numbers i :from 1 :to 10)
(collect i)
(finally-return i loopy-result))
```
one could instead write
```emacs-lisp
;; => (5 (1 2 3 4))
(loopy (with (i 1))
(list elem '(1 2 3 4))
(while (<= i 10))
(collect i)
(set i (1+ i))
(finally-return i loopy-result))
```
which is basically the same length while requiring less knowledge of each
optimized iteration command's internals.
There are already cases where Loopy is not hyper-optimized, such as explicit
accumulation variables. However, there, the behavior of `accum-opt` is much less
of a burden to remember, because it affects every accumulation command in the
same way. There are no special cases to remember.
For these optimizations, it is the case that Loopy's flexibility and consistency
has a small cost, but they are not the only case of that (consider how `cl-loop`
moves some variable setting to `and` expressions while we settle for `catch` and
`throw`) and we do not question the value of consistency and predictability in
those cases.
0 commit comments