[Openmcl-devel] LOOP parallel for/as termination eval order issue.
Kaz Kylheku
kaz at kylheku.com
Mon Oct 18 14:53:39 PDT 2010
On Mon, 18 Oct 2010 05:56:47 -0600 (MDT), Gary Byers <gb at clozure.com>
wrote:
> If you think that X can be initialized to some predictable value before
> the termination test, what on earth would that value be ?
I believe that the purpose of the text ``meaningless value
of the correct type'' in 6.1.1.4 must be to support type
declarations. Since we aren't declaring X to have some
type (no [type spec] is present in the clause) its value must
be NIL.
If the following doesn't return NIL, I believe there
is a problem.
(loop for x in () finally (return x))
Do some Lisps return something other than NIL?
I can get CCL to return non-NIL like this:
(loop for x of-type float in ()
finally (return x))
-> 0.0
CLISP, same thing.
Another remark is that In Common Lisp it is
idiomatic to inquire about the first element of an empty list
with (first nil) without caring that it's empty.
In the absence of a type-spec, X could simply be initialized
using (car <list>) without caring that list may be empty.
> Even CLISP believes that the termination test has to precede assignment to
> X.
My reaction is that "termination" can refer to "termination of the
list"
as well as "termination of the loop". Just because we have to test
list termination doesn't imply that we must also
terminate the loop right there.
> The issue is that CLISP sometimes decides that the assignment to Y can be
> moved to a point before the assignment to X and before the termination test
> and that other times it can't.
I'm now regarding that inconsistency should be regarded as a bug.
Be the spec as it may, the reality is that Lisp implementations
implement
LOOP in a certain way (perhaps traditional or whatever), and so that
is what we have to follow in the trenches.
Wishing that it was different isn't going to magically cause a
dozen implementations (or whatever) to fix themselves.
My program probably worked by dumb luck by relying on buggy
CLISP behavior which superficially looked like it was
implementing the termination-test deferring behavior
that is allowed by the spec, when in fact what may be going
on is whimsical.
Though everything works, I still want to make sure that what I'm
doing in my program is (de-facto) portable.
After this debate, I think that the portability recommendations
can be tentatively summarized like this:
- a loop iteration control clause can trigger loop termination
during variable initializations or stepping, causing subsequent
clauses not to be processed, leaving their implied
initializations or steppings not done. The implied effects
of prior forms are done, however. So for instance if
there are two clauses:
for x in form1 then form2
for y in list
and it happens that list runs out of elements, then
"for y in list" may trigger instant loop termination
when it detects that the loop has no more elements.
However, the effect is already settled that X has
received the value of FORM1 (if the first iteration of
the body has not yet occured) or FORM2 (if this is just
prior to what would have been the second or
subsequent iteration).
- thus, variable assignment and steppings made prior to the
terminating-triggering clause are stable, and their
values and other side effects can be used in the epilogue
(finally clauses)
- variable steppings or assignments following the clause which
triggered termination may or may not made.
Some implementations (notably CLISP) sometimes
reorder the clauses, such that the termination is done later.
Deferring termination tests does not violate the letter
of the spec whose wording strongly suggests that in fact
termination tests can be deferred. (Although doing this
inconsistently can be regarded as buggy behavior nevertheless).
Thus, extending the prior example, if we have:
for x in form1 then form2
for y in list
for z = (pop other-list)
if "for y" triggers termination, the (pop other-list)
call and update of z may or may not take place.
So not only may you not rely on the value of Z
in the loop epilogue, but also not on the state
of the variable OTHER-LIST.
(So for portability, don't do stuff like this!)
- When multiple clauses are connected by AND and one of
them triggers termination, it is probably best not
to rely on any of variable updates implied by the
others to have occured (or not to have occured). The
safest assumption is that all bets are off.
Avoid AND when surrounding variables are
being accessed in a FINALLY.
More information about the Openmcl-devel
mailing list