Why loop when you can https://iterate.common-lisp.dev/ instead? No s-expr-less alien syntax, no need for `do` to switch to back to Lisp syntax, normal `if`/`when` without the ugly `else`/`end` and generally useful features added.
If I used Common Lisp more I'd probably have a go at copying Racket's `for` forms[1]; they're really nice because you can usally tell at a glance what they're going to return - `for/list` returns a list for example. No having to scan the body for a `collect`.
But in the meantime since discovering iterate I've barely used `loop`. It just feels so much more lispy and I find myself running to the documentation less often.
Interesting concept, but it visually has the same problem as loop IMO, using keywords to implement a new syntax instead of seamlessly blending with Lisp (at the cost of needing code walking, though).
And it seems to lack all the iterations drivers (incl. builtin destructuring) that make half of loop/iterate's usefulness and "reads like English" comfy factor; especially liking
(for (i j) on list [by #'cddr])
(for i initially init-expr then then-expr)
(for prev previous i [initially init-expr])
(for i in-{file,stream} [using #'reader])
Racket splits up the iteration forms from what to iterate over (sequences[1]). You can compose different sequence constructors together, or make brand new ones, without introducing new syntax.
It has limited destructuring - sequences can return multiple values, all of which can be bound. There's an adapter to convert one that does that into returning a single list, but not the other way around. If there was it could be used with `in-slice` to be equivalent to your first example.
I could probably write a new sequence to get the `previous` behavior; don't think `initially ... then` is possible.
Lots of sequences for reading from open ports (the Racket/Scheme name for CL streams)... `(for ([i (in-port)]) ...)` for example (with an optional reader argument defaulting to `read`).
Ah, I see, though I'd say it pollutes the function namespace a bit this way (as "in-x" semantically only makes sense in a loop) and missing on-list. Technically, you could do most of these in a few lines of CL too, but well, convenience is the point of these macros.
Those seem to return sequences instead of streams/iterators, any idea why? Though it says "An in-list application can provide better performance for list iteration when it appears directly in a for clause", so I guess there's some macro magic at play.
Anyway, thanks for exposing those, Racket does seem to be pretty practical (and with its Chez backend, I guess it's pretty fast); can't stand the square brackets used as syntax (as opposed to vector literals used as data), though ¯\_(ツ)_/¯.
I don't understand why turning a simple loop into a 'mindbend' is considered good. The downfall of programming is complexity, if you're getting your mind blown by a loop how are you going to do the rest of the program?
mindbending can also refer to something being deceptively simple. you might think it would be a big complicated mess, but using this one weird trick makes it really obvious what's going on.
How does that relate to a simple loop construct though? Why would you want that to be mind bending in interface or implementation? Every other language makes it as simple as possible.
This isn't really true – you have languages like Odin that only have a for loop, no while loop, that only supports index-based iteration. Then you have languages like Python that let you loop over an arbitrary iterable, and define your own iterables. Some languages allow conditionals in loops, some don't. Some let you loop over multiple iterables, while some only take one at a time.
Common Lisp happens to be on the upper end of what loop allows – you can use it as a standard for loop pretty easily, but the interface gives you many other options.
The mindbend was more of my approach to the construct. It began with disdain before even really using it much. Looking back, I really couldn't articulate what I disliked about it.