2 ms·
> The designers thought that a whole new language would be useful. Yes, obviously they thought this. They were wrong. > ITERATE Iterate is a slight improvem
by lisper 4y ago
> The designers thought that a whole new language would be useful.
Yes, obviously they thought this. They were wrong.
> ITERATE
Iterate is a slight improvement over loop because at least it doesn't require a whole new editor mode to format it properly, but simply adding parens to loop doesn't really fix it. The lack of parens is the least of LOOP's problems.
I'll give you two more examples of problems with loop. This is still nowhere near an exhaustive list.
1. There is no way to iterate over a sequence. You can iterate over a list, or you can iterate over a vector, but you cannot efficiently iterate over something that is either a list or a vector. You have to write something like:
(typecase seq
(cons (loop for item in seq ...))
(vector (loop for item across seq ...)))
which forces you to duplicate all of the LOOP code twice. Worse, you have to actually duplicate all the code represented by the elipses. You can't abstract it away in a function or a macro.
2. LOOP is not extensible. There are a lot of things I might want to be able to loop over, like streams, but I can't. There are a lot of control constructs I might want to embed in a loop, like with-open-file, but I can't. Instead, I have to resort to manually writing idioms like:
(loop
with stream = (open path)
with eof-marker = (gensym "EOF")
for thing = (read stream nil eof-marker)
while (not (eq thing eof-marker))
...
finally (close stream))
and again, because of the non-orthogonal syntax, I can't abstract all that away in to a macro either. I have to write it all out manually every single time I want to iterate over the contents of a file.
(And after all that it doesn't even do the right thing if the loop code signals a condition!)
- mojifwisi 4y agoIterate doesn't have either of these problems, so I don't see how it's just a "slight improvement". > There is no way to iterate over a sequence. With iterate, you'd do: (iter (for item in-sequence seq) ...) > LOOP is not extensible. But iterate can be extended with DEFMACRO-CLAUSE.[1] I think that's why iterate added the parentheses in the first place. [1]: https://iterate.common-lisp.dev/doc/Rolling-Your-Own.html https://iterate.common-lisp.dev/doc/Rolling-Your-Own.html
- lisper 4y ago> I don't see how it's just a "slight improvement" I'd probably have to write a whole blog post to explain why I think iterate is only a slight improvement. But the TL;DR is that IMHO if you are writing code that uses a lot of the features of iterate or loop that is an indication that you are doing something wrong. To cite but one example: both loop and iterate include constructs for collecting values. But collecting values has nothing to do with iterating or looping. It should be a separate construct. The right way to collect values is something like: (with-collector collect ... (collect value) ...) Now you can collect values whether or not you are looping, and regardless of what iteration construct you decide to use. You don't need special constructs for conditional behavior. So, for example, you could do this: (with-collector collect (dotimes (i 100) (if (primep i) (collect i))) to get a list of primes under 100. See https://github.com/rongarret/ergolib https://github.com/rongarret/ergolib for an implementation of WITH-COLLECTOR and lots of other constructs that are IMHO the Right Way to write code.
- lispm 4y ago> But collecting values has nothing to do with iterating or looping. It should be a separate construct. I don't care about such rules. Personally I have no problem using: (loop for i below 100 when (primep i) collect i)
- lisper 4y ago> I don't care about such rules. Yes, that is clear. > Personally I have no problem using: Loop is fine for simple examples like this. But I am currently maintaining a code base that has LOOPs with dozens and dozens -- sometimes a few hundred -- clauses. A single LOOP can extend over multiple pages. It's a nightmare. Note that LOOP can fail even for simple examples. Suppose I have a list of lists of numbers and I want to collect all the prime numbers. With WITH-COLLECTOR I can do this: (with-collector collect (dolist (l1 l) (dolist (n l1) (if (primep n) (collect n))))) But with LOOP I can't because there is no way for an inner loop to collect into a collector bound in an outer loop. I have to collect the individual sub-lists and then append them, or something like that, which is both inelegant and inefficient. And if I have a tree of items which I want to walk over and collect all of the once satisfying a predicate, LOOP just doesn't handle that at all. But by separating collection from iteration it becomes trivial: (with-collector collect (do-tree (item tree) (if (predicate item) (collect item)))) Neither WITH-COLLECTOR and DO-TREE are part of CL, of course, but writing them is an elementary exercise (and both are part of ergolib if you really don't want to be bothered).
- lispm 4y ago> LOOP is not extensible The standardized LOOP is not extensible. Each LOOP implementation is extensible. A common extension is to provide new loop iteration paths. LispWorks: CL-USER 7 > (lispworks:defloop (s-element s-elements) elt length sequence t) (S-ELEMENT S-ELEMENTS) Now we can iterate over sequences in a not performance optimized version: CL-USER 8 > (loop for e being each s-element of "hello-world" count e) 11 CL-USER 9 > (loop for e being each s-element of '(hello world) count e) 2 There are portable extensible LOOP implementations. This is then just as non-standard as any self-defined iteration library. https://research.gold.ac.uk/id/eprint/2344/1/sequences-20070301.pdf https://research.gold.ac.uk/id/eprint/2344/1/sequences-20070... gives an example to extend SBCL's LOOP (which is based on the MIT LOOP implementation). There are lots of things in the Common Lisp standard, which are not extensible, but where implementations and libraries provide extensible versions. Example: the CLOS MOP, sequences, hash tables, ...
- lisper 4y ago> Each LOOP implementation is extensible. Allegro isn't. Neither is SBCL. Nor CCL. Nor CLisp. In fact, the only implementation that offers this feature AFAICT is Lispworks. > There are lots of things in the Common Lisp standard, which are not extensible Sure. So? Just because loop is not the only thing in CL that sucks does not make it suck any less.
- lispm 4y agoPretty much all of those are extensible, especially given that a) they are provided in source code and b) those LOOP macro implementations have internal extension features. The paper I've linked gives an example how to extend LOOP in SBCL for iteration over sequences.