So I've been thinking about this naming issue since at the moment I am somewhat blocked for names. It seems to me that there are two orthogonal features which are somewhat tied together in our current closure system:
- how the environment is referenced;
- what data the environment contains.

A unique closure references its environment by a unique pointer (~) and only permits the environment to contain sendable data. A boxed closure (lambda) references the environment via a box pointer (@) and only permits the environment to contain copyable data.

I am not proposing we separate these things: I don't think there is a need to have a unique pointer to an environment that closes over copyable data nor boxed closures that close over only sendable data. However, the choice of names for unique/boxed closures will probably depend on which of these two features we wish to emphasize.

I like the idea that "fn" is just the type of a general closure (i.e., "block"). I agree it's what you want most of the time. I think that there are then three logical choices for boxed/unique closures:

1. `fn@` and `fn~`: this is intended to emphasize the fact that the environment is referenced by a boxed/unique pointer. I don't know how intuitive it is and it makes heavy use of sigils.

2. `copyfn` or `sendfn`: this emphasizes the kind restrictions. This is also nice because a `copyfn` is copyable and a `sendfn` is sendable, whereas a plain `fn` is neither.

3. `fn[copy]` and `fn[send]`: an alternate version of #2 that I find more visually appealing.

Of these, I prefer `fn[copy]` and `fn[send]`. My only reservation is that the syntax meshes reasonably somewhat awkwardly with capture clauses in the copy case. A capture clause is used to specify variables you wish to copy/move out of the environment; if we moved to `fn[copy]` and `fn[send]`, it will also specify the kind restriction. So, you would write:

let block_1 = fn(x: T, y: U) -> V { ... }; // currently not legal but should be, I think
    let boxed_closure_1 = fn[copy](x: T, y: U) -> V { ... };
    let unique_closure_1 = fn[send](x: T, y: U) -> V { ... };

and if you wanted to copy the variable `a` out of the environment explicitly and move the variable `b`, you would write:

    let boxed_closure_2 = fn[copy a; move b](x: T, y: U) -> V { ... };
let unique_closure_2 = fn[send; copy a; move b](x: T, y: U) -> V { ... };

Here you can see that the kind restriction melded with the request to `copy a`. At the moment, explicit copies are unnecessary anyhow, but I personally would like to make them required for mutable local variables or for variables whose types are not implicitly copyable (per the no copies proposal I sent out a while back).

Thoughts?


Niko

On 12/20/11 2:50 PM, Brian Anderson wrote:

----- Original Message -----
From: "Niko Matsakis"<[email protected]>
To: [email protected]
Sent: Tuesday, December 20, 2011 2:13:49 PM
Subject: [rust-dev] names needed for function types
Per recent conversations, my current plan with regard to function
types
is to pare us back to three:

- Unique closures (currently written "sendfn"), which can only close
over sendable state, which is either copied or moved into the closure;
- Shared closures (currently written "lambda" or "fn@"), which can
copy
or move arbitrary state off the stack frame and access it;
- Blocks (currently written "block"), which access state from their
creator's stack frame by reference and therefore cannot be used once
their creator returns.
This will mean 'fn' can't be used as a type, unless it means two different 
things - I don't think it should both mean 'shared closure' and this non-type 
thing that can be instantiated as any type of closure. It's a shame that we 
have to think of a different name for the most common function type, but maybe 
not a big problem since you often want to use it as a block.

The question is: what should we call the various closure types? I
think
we need both good prose names and good keywords, ideally those are the
same.

Rename 'block' to 'fn'. Reason: this is the type that most higher order 
functions take; it has symmetry with your proposal for fn declarations, in that 
fn declarations can be instantiated as either shared closures or unique 
closures. In common practice you would write 'fn' most everywhere. Only when 
you need to put a closure into a data structure or send it would you have to be 
more specific about the type.

Still thinking about the other two.

_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to