On 29/09/2011 5:28 AM, Patrick Walton wrote:
On 9/28/11 9:17 PM, Graydon Hoare wrote:
- Expresses the "iteratee is aliased during iteration" fact to the
alias-checker, so you don't have to worry about invalidating
externalized iterators. This is important; particularly if you want
to exploit next part ...
I don't understand this, sorry... could you explain?
Sure. I want to make sure it's clear:
let v = [1,2,3,4];
let i = vec::iter(v);
v = [];
while (i.has_next()) {
let e = i.next();
foo(e); // crash.
}
Same thing happens if you mutate an element:
let v = [mutable [0],[1],[2],[3]];
let i = vec::iter(v);
let e = i.next();
v[0] = [];
e[0]++; // crash.
You're introducing either require-copy or unsafe-alias problems.
Closure-passing style tells the alias-analysis system what it needs to
keep reference-based iteration safe. You can't touch the vec while
you're pointing into it. Statically. Even if you make copies, it's very
likely nothing you return *from* the iterator can be a reference either
since they'll be hidden state inside the iterator; the alias-analysis
system can't make unsafe code safe.
We can't do this with exterior iters.
What about:
... (lambda holding unsafe pointers) ...
The version I wrote is actually safe (you can't crash it since the vec
is kept alive during iteration); the one you wrote is contingent on the
programmer "using it right". Alias-analysis problem, as above.
The complexity of loopctl is what I'm worried about, I guess. It
requires a bunch of dynamic "did this loop break? did this loop
early-return?" checks. I'm not sure what the precedent for this is.
Lots. Lots and lots. Many functional interfaces define a "return false
from thunk to stop the enumerator" style. Early returns from non-total
sequence enumerators in side-effecty functional languages have been
around at least since common lisp:
http://www.lispworks.com/documentation/HyperSpec/Body/m_dolist.htm
Likewise it's in SRFI-44
http://srfi.schemers.org/srfi-44/srfi-44.html#enumprocs
Those less-side-effecty languages (haskell and ML say) always have a
search-and-return-early "find" interface too:
http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.4.0.0/Data-List.html#v:find
http://www.standardml.org/Basis/list.html#SIG:LIST.find:VAL
If you want to do the simpler thing, we don't need a "full" tri-state
loopctl datatype, just bool. Do this:
- Break in loop body => return false from loop thunk
- Cont in loop body => return true from loop thunk
- Ret in loop body => prohibited, but easy to emulate:
let res = sentinel;
let found = false;
for e in iter(v) {
if foo(e) {
res = e;
found = true;
break;
}
}
if found { ret res; }
Another possibility I'm totally fine with:
- Eliminate 'for / break / cont' transformation altogether,
just tell users to write in terminal-bool closure-passing style:
let res = sentinel;
let found = false;
vec::each(v) {|e|
if foo(e) {
res = e;
found = true;
false
} else {
true
}
}
if found { ret res; }
lest you find this too chatty for the no-early-stop case, let
me point out aspect B:
- Write >1 iter interfaces on most collections, with varying
strategies, to reduce unwanted boilerplate in cases you don't
need it. Cold code is cold, harmless.
vec::each(v) == takes fn(e) -> (), returns ()
vec::scan(v) == takes fn(e) -> bool, returns ()
vec::find(v) == takes fn(e) -> bool, returns option::t<e>
I really, really think the exterior-iterator style is wrong. It puts
logic in the wrong place, encourages hand-recoding loop logic
incorrectly rather than collecting a rich library of correct
enumerators. You can write a whole lot of cold enumerators that are safe
and do-interesting-things to cover, I think, any of the needs of an
external-iterator. This was less true when we had no closures, but now
that we have block closures, I don't think there's any excuse.
If you don't believe me, believe Oleg!
http://okmij.org/ftp/papers/LL3-collections-enumerators.txt
-Graydon
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev