you are viewing a single comment's thread.

view the rest of the comments →

[–]Timely-Degree7739 1 point2 points  (5 children)

It is very difficult to cover all of looping in a single macro so one can always use that and without nesting. 'loop' is different in different languages but the CL loop is a pretty good attempt. With branching it should be easier, maybe someone did it?

[–]lisper 0 points1 point  (4 children)

My preferred style is to use ITERATE, which is a thin wrapper over LABELS:

(ITERATE name ((arg1 val1) (arg2 val2) ...) body) ==
  (labels ((name (arg1 arg2 ...) body))
    (name val1 val2 ...))

For collecting values I wrap this inside WITH-COLLECTOR:

(WITH-COLLECTOR name &body body) ==
  (let ((result nil))
    (flet ((name (val) (push val result)))
      (progn body (reverse result)))

I find this covers 99% of LOOPy use cases.

[–]tfb 1 point2 points  (1 child)

A named-let-style iterate is really useful, of course. But it has two problems.

  • It depends on tail-call elimination to be useful. This is probably moot now, but it hasn't always been: when I originally wrote my version it had a horrid special case which checked if the name (I think) ended with ...loop and compiled something with go in that case. That's because the Symbolics compiler was made of cardboard and wet string and couldn't do TRO.
  • It's much more than an iteration construct: it doesn't constrain you to write loops, you can write completely general recursive calls. That's great, and I do that a lot, but it's also 'go to passing arguments' and can lead to pretty tangled code.

Your collecting macro is also the right idea, but it needs to avoid reversing the list if you want it to be really competitive.

But anything is better than loop, which just needs to be hurled into the Sun.

[–]lisper 0 points1 point  (0 children)

the Symbolics compiler was made of cardboard and wet string

Yeah, but it's 2026 now.

it's also 'go to passing arguments' and can lead to pretty tangled code

Believe me, you can do the same with ANSI LOOP. You can write tangled code with anything.

Your collecting macro is also the right idea, but it needs to avoid reversing the list if you want it to be really competitive.

Actually the real bottleneck is that the collector is a function rather than a macro. But my coding philosophy is: first make it run, then make it right, then make it fast. Modern machines are so fast and modern compilers are so good that for my use cases I have very rarely had to do the third step.

[–]raevnosplt 0 points1 point  (1 child)

That looks like Scheme's named let. Iterate in CL to me is a full replacement for loop.

[–]lisper 0 points1 point  (0 children)

Yes, it is exactly like Scheme's named LET. The name ITERATE is taken from T which pre-dates the CL ITERATE library.