Ah, I see now that nested functions cannot access upvars. I was under
the impression this limitation had been removed. I did not intend to
take away access to upvars (as you and Graydon said, crucial to event
callbacks) but just the anonymous function syntax.
However, I've been thinking it over more since last night and now I
think that this is not a very good idea. It'll make the language feel
heavy and we want it to feel light. What might be nice, then, is a less
ponderous syntax than lambda syntax.
In any case, I am still toying with what unique closures ought to be.
One problem I have uncovered (and it's already a bug today) is that a
unique closure must be very careful what it does with its unique,
closed-over state. The problem is that a unique closure can be invoked
multiple times. Not so good if you are moving or dropping unique
variables.
For example, this code crashes today:
fn foo(-x: ~int) { }
fn main() {
let b = bind foo(~3);
b();
b();
}
The error is that bind needs to "re-copy" the ~3 each time `b()` is
invoked, but it doesn't. In other words, that bind code is equivalent to
`let x = ~3 in fn () -> foo(x)` (in some weird O'Caml/Rust hybrid) but
it would need to be `fn() -> let x = ~3 in foo(x)` to be safe.
This problem can be addressed in many ways and I am not sure what is
best. One option is to say that unique closures cannot move or dispose
of their upvars (here, bind is moving its upvar, essentially).
Essentially upvars become like a field of a record or other immutable
location, which must be accessed via swap. This is safe but somewhat
disappointing, because most unique closures will be used to spawn a task
and therefore executed only once.
Another option is to specify that a unique closure can be invoked only
once (calling it consumes itself). This is also limiting if that's not
what you want, but it probably is. In particular it does not allow
unique closures to become the foundation for a map/reduce library.
All in all, I am just not sure that unique closures are the right tool
for a "user-friendly", simple parallelism API, which I understood to be
one of their goals. In other words, if you want to fork off a bunch of
tasks to process the various elements in your array and then pull them
back in, unique closures don't really give you the guarantees you want.
On the other hand, unique closures do serve a useful purpose for
spawning off a task and handing it data. This could also be simulated
today using a wrapper that creates a channel, starts a new task, passes
the value over the channel, and finally invokes your function. Of
course this adds overhead for something that (may?) be a common case.
But I am not sure if moving one-off data like that *is* a common case.
Niko
On 12/5/11 8:33 AM, David Rajchenbach-Teller wrote:
On Mon Dec 5 14:27:18 2011, Marijn Haverbeke wrote:
If bind is staying (there was talk of removing it in favour of lambda
at some point), I think we can do without lambda. But the concept of
shared closures (which is what bind creates) would still be there.
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev
What would be removed exactly? I strongly believe that we need a form of
closures, e.g. for registering event callback, whether to a GUI, or for
network or system events.
Now, of course, as long as we have some ability to pair a data structure
and a function pointer, and a standard way to represent functions
expecting this kind of data structure, we can always fallback to manual
closure conversion, at the expense of some readability.
I wonder, what exactly would be gained by removing lambdas?
Cheers,
David
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev