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
[email protected]
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
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to