![](https://secure.gravatar.com/avatar/2f7ec3b37425cf4e077e7ef376e451be.jpg?s=120&d=mm&r=g)
Bruno Daniel wrote:
the following example shows that iter initializes sequence variables twice, once with 0 and once with the start of the range minus 1.
To me this sums up to 2 questions o Why generate (let ((#:count nil/0)) (setq #:count 5) ...)? That maybe has to do with an answer to whether the following two expressions mean the same thing, i.e. whether INITIALLY can still influence clauses like FOR and REPEAT. It probably has to do with how Iterate creates bindings. A single LET* has some restrictions... (iter (with i = 5) (repeat i) (count t)) (iter (with i) (initially (setq i 5)) (repeat i) (count t)) As I found no place in the documentation about whether or not the latter form should be supported, I'm somewhat reluctant to change the behaviour. Iterate is a 18 year old package, so there could be code which depends on this. Furthermore, although let + setq does not look pretty, it is possible that advanced compilers like cmucl and sbcl, which perform some liveness analysis, will note that the 0 value is thrown away and not emit code for it. So performance critical code compiled with performance enabling compilers does not suffer from that extra initialization. CLISP does no such optimization. I don't know how others behave. o Why generate weird code where some initial value is set to 1+ or 1- what one would expect? E.g. in (for e in-vector ...), #:index is initialized to -1. "That breaks type declarations, since one would expect index to be a positive fixnum." I believe this is due to the generators. Drivers are useable as generators, and these are all translated into a sort of (for... (DO-)NEXT body) expression, so that the body can be spliced into the (NEXT ...) clause of a generator case. In other words, Iterates now deals with code that must produce the initial value upon the first call, e.g. 0 when first called. To keep the stepper code simple, since it's often nothing more than (incf #:index) with some end-test, the variable is thus initialized with -1, or (1- expression). Probably it was deemed inacceptable back when that was implemented to make the stepper code more complex, as in (setq index (if (first-time-p) #:initial-index (1+ index))). That for sure is less efficient than a -1 initialization. Now, I don't know for sure whether that's the only reason for doing so. One can then claim that if it's the only reason, Iterate could detect whether the clause is actually used as a generator, and produce different code depending on the use as generator or not. E.g. use -1 with generators, but something like (for x initially 0 then (1+ x)) when not used as generator. Well, I appreciate that people look closely at what Iterate does, but sometimes I wonder why more people come up with requirements that would make the code even more complex, and fewer people with ideas about how to make it simpler :-) Still, contributions are welcome!
Moreover, the declaration is not very tight. dotimes does better on SBCL: (macroexpand-1 '(dotimes (i 10) (print i))) (DECLARE (TYPE UNSIGNED-BYTE I)) Perhaps because on SBCL, such precise declarations matter, so SBCL goes to some length to generate them? At the time Iterate war written, all that mattered was probably FIXNUM, at best.
As far as SBCL is concerned, I'm not even sure such declarations are needed, since one would hope that cmucl/sbcl's type inference system would derive that from the code. Furthermore, loops with constant bounds are a very special case. So it's debatable whether to add declarations for such special cases. Given good type inference, sbcl/cmucl might even be able to infer best types for (repeat <formula>) expressions, and no macro-based package can generate a good type declaration for that. In short, I think the best place for handling types is the implementation's compiler. Sort of separation of concerns. Unless the user knows better than the compiler. But macro packages do never ever have the user's knowledge. Regards, Jörg Höhle.