Hacker News new | ask | show | jobs
by mojifwisi 1345 days ago
Iterate 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

1 comments

> 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 for an implementation of WITH-COLLECTOR and lots of other constructs that are IMHO the Right Way to write code.

> 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)
> 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).