10 ms·
> You don't need to design a whole new language to solve this non-existent problem. The designers thought that a whole new language would be useful. One may di
by lispm 4y ago
> You don't need to design a whole new language to solve this non-existent problem.
The designers thought that a whole new language would be useful. One may disagree with it. I would have preferred something like the ITERATE macro, but it has exactly the same idea of ENCLOSING all of these LOOP clauses, but with more parentheses.
(iterate
(with a = 1)
(incf a)
(when (> a 10)
(return a)))
is
(loop with a = 1
do (incf a)
when (> a 10)
return a)
If one cares about radically different syntax then you wouldn't use LOOP at all. (loop for i in l do (print i)) is equivalent to (dolist (i l) (print i)) which is equivalent to something with a LET/TAGBODY... construct. Why not just use the latter? Maybe its design choices made it practical enough?
- 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).
- pfdietz 4y agoITERATE is nice, except for one very serious blemish: the accumulation into variables requires a code walker, because the variables are not declared at the top of the iterate form. Macros that require code walkers do not play nicely with other kids. Macrolet/symbol-macrolet, for example, can fail inside interate.