dotimes* is dotimes without an iteration variable. You
might question the relevance of having such a minute variation on
dotimes available. It comes down to a simple question of
completeness. Loopless strives to always provide an unambiguous “best
operator” in situations where loop has an answer and the rest
of Common Lisp either doesn't provide an answer or provides a few
different answers with no clear winner.
If dotimes* (and the maptimes* family of functions)
didn't exist, each time I wanted to loop a certain number of times
without caring for the iteration variable, I'd curse having to name
the variable and be tempted to use loop for its repeat
clause just this one time. I'm a really lazy programmer,
and as far as possible, I don't want to have to think for
recurring scenarios!
(dotimes* (3 :done) (write-string "he"))
==
(loop repeat 3 do (write-string "he"))
-| hehehe
⇒ :DONE
Here's how to read forms from a stream with while*.
This may not look much better than the loop way at first, but
notice that each of collecting/collect, let,
while* and setf does only one thing and can be used with
the rest of the language.
Also, let and setf are conveniently already part of the
language.
(with-input-from-string (stream "some (simple \"forms\") #(to read)")
(collecting
(let (form)
(while* (setf form (read stream nil nil))
(collect form)))))
==
(with-input-from-string (stream "some (simple \"forms\") #(to read)")
(loop for form = (read stream nil nil)
while form
collect form))
⇒ (SOME (SIMPLE "forms") #(TO READ))