Discussion:
iterate on list bug?
Gábor Balázs
2014-05-21 07:06:30 UTC
Permalink
hY,

I have a feeling this is a bug.

(defvar mylist '(:one :two :three))

(iter (for var on mylist)
(collect (car var) into result)
(finally (return (values result var))))
(:ONE :TWO :THREE)
(:THREE)

I think var should be empty at the end, so the result would match the
result of loop as

(loop for var on mylist
collect (car var) into result
finally (return (values result var)))
(:ONE :TWO :THREE)
NIL

The problem is caused by the end test code location.
The bold lines should appear in reverse order, I think.

(macroexpand-1 '(iter (for var on mylist)
(collect (car var) into result)
(finally (return (values result var)))))
(LET* ((#:LIST214 NIL)
(VAR NIL)
(RESULT NIL)
(#:END-POINTER215 NIL)
(#:TEMP216 NIL))
(BLOCK NIL
(TAGBODY
(PROGN (SETQ #:LIST214 MYLIST))
LOOP-TOP-NIL
(PROGN



* (IF (ATOM #:LIST214) (GO LOOP-END-NIL)) (SETQ VAR
#:LIST214)* (SETQ #:LIST214 (CDR #:LIST214))
(PROGN
(SETQ #:TEMP216 (LIST (CAR VAR)))
(SETQ #:END-POINTER215
(IF RESULT
(SETF (CDR #:END-POINTER215) #:TEMP216)
(SETQ RESULT #:TEMP216)))
RESULT))
(PROGN)
(GO LOOP-TOP-NIL)
LOOP-END-NIL
(PROGN (RETURN (VALUES RESULT VAR))))
NIL))
Best,
` bg`
J***@t-systems.com
2014-05-21 09:45:05 UTC
Permalink
Post by Gábor Balázs
The bold lines should appear in reverse order, I think.
       (IF (ATOM #:LIST214)
           (GO LOOP-END-NIL))
       (SETQ VAR #:LIST214)
You can't invert the order, because that would be a type violation.
Iterate may make use of types, see *declare-variables*.

Furthermore, the spec does not guarantee that value after the end of the iteration.
Quite to the contrary, it's explicitly disallowed. Quote:

"In all cases, the value of the driver variable on exit from the loop,
including within the epilogue code (see the finally clause), is
undefined."

Regards,
Jörg Höhle
Gábor Balázs
2014-05-21 15:33:15 UTC
Permalink
Post by J***@t-systems.com
Post by Gábor Balázs
The bold lines should appear in reverse order, I think.
(IF (ATOM #:LIST214)
(GO LOOP-END-NIL))
(SETQ VAR #:LIST214)
You can't invert the order, because that would be a type violation.
Iterate may make use of types, see *declare-variables*.
Furthermore, the spec does not guarantee that value after the end of the iteration.
"In all cases, the value of the driver variable on exit from the loop,
including within the epilogue code (see the finally clause), is
undefined."
This is indeed a surprise for me.
Definitely should be on the "Differences between LOOP and iterate" page.

But I think iterate should make the drivers accessible in the epilogue.
This code is not that nice and confusing.

(iter (for var on mylist)
(for var2 = (rest var))
(collect (car var) into result)
(finally (return (values result var2))))
(:ONE :TWO :THREE)
NIL

So is there some design reason against making the drivers accessible in the
finally clause?
Maybe, could just iterate parse the finally clause and make the proper
adjustments if necessary?

`bg`
Post by J***@t-systems.com
Regards,
Jörg Höhle
J***@t-systems.com
2014-05-21 16:17:20 UTC
Permalink
So is there some design reason against making the drivers accessible in the finally clause?
I think it's simply more robust to locally roll your own iterate extension that exhibits the behavior you need using the defined extension API, rather than produce an incompatible Iterate with an obscure change in semantics. Mind dll hell!

Do you want to debug a large app that exhibits an obscure bug that would eventually be traced down to the user using a version of the Iterate library that differs from yours in that aspect?
Definitely should be on the "Differences between LOOP and iterate" page.
Good point. Who submits a patch to the LaTeX documentation?

Regards,
        Jörg Höhle
Zach Kost-Smith
2014-05-21 18:04:53 UTC
Permalink
It's worth remembering that this is undefined behavior. It is usually safe
to start defining behavior in undefined cases as they should be, by
definition, backwards compatible.

To be honest, this is not an aspect of Iterate that I was aware of. It is
a bit surprising that this is undefined behavior. Gabor's examples are a
bit theoretical, but if my understanding is correct, the following
functions have undefined behavior.

(defun split-on1 (obj list &key (test 'eql))
(iter (for (x . list-b) :on list)
(collect x :into list-a)
(until (funcall test obj x))
(finally (return (list list-a list-b)))))

...or, if you prefer that the element goes on the second list,...

(defun split-on2 (obj list &key (test 'eql))
(iter (for (x . list-b) :on list)
(until (funcall test obj x))
(collect x :into list-a)
(finally (return (list list-a (cons x list-b))))))

I suppose that the specification states that both of these are undefined
(due to the usage of x in on-split1 and x and rest in on-split2), though
some testing seems to suggest that they work. Seems like defining the
bindings would be a step in the right direction. Am I missing something?
Post by Gábor Balázs
So is there some design reason against making the drivers accessible in
the finally clause?
I think it's simply more robust to locally roll your own iterate extension
that exhibits the behavior you need using the defined extension API, rather
than produce an incompatible Iterate with an obscure change in semantics.
Mind dll hell!
Do you want to debug a large app that exhibits an obscure bug that would
eventually be traced down to the user using a version of the Iterate
library that differs from yours in that aspect?
Post by Gábor Balázs
Definitely should be on the "Differences between LOOP and iterate" page.
Good point. Who submits a patch to the LaTeX documentation?
Regards,
Jörg Höhle
_______________________________________________
Iterate-devel mailing list
http://common-lisp.net/cgi-bin/mailman/listinfo/iterate-devel
Loading...