In principle, I'd prefer to have a lambda form that can implicitly bind upvars,
although I think I'd look at this from a slightly different direction than
Sebastian. I think RAII has a lot of good things going for it, and try/finally
is weak tea. But I don't think lambda is just about RAII. In particular, I
think a very common use case for lambda is for event-driven programming, a
style I'd expect to come up often in Rust programs. Requiring programmers to
name their functions and place them out-of-band breaks up the flow of the
programming and just makes it a little harder to read. Or from the other
direction: being able to pass an event handler directly inline as an anonymous
lambda makes it immediately clear to the reader that the relevance of the
function doesn't extend beyond this one place.
Graydon, Re: control flow complexity, I'm not sure whether lambda adds too much
control-flow complexity, given that we already have higher-order constructs
with |bind| and objects. Primarily, I see it as a lightweight notational
convenience for a common pattern, which you can already express using helpers.
But all that said, nobody's thought through Rust's control flow as deeply as
Graydon, and I wouldn't swear to it that there isn't some deal-breaker I
haven't thought of. Here are a few thoughts about what seem like some of the
tricky issues:
- Exteriors:
I wouldn't think we'd want to allow lambda-functions to close over
stack-allocated locals. Stack locals are not meant to outlive their frame, and
lambda-functions are. IIRC, Apple's GCD wantonly lets you do so, with C's usual
"it's undefined" as the semantics of referring to a dead upvar. That's
obviously not an option. Java achieves safety by forcing you to either
const-declare variables that have upvar references, so they can be copied, or
else effectively box/heap-allocate them by placing them in objects or
one-element arrays.
We can do better than Java, though, since we have a really lightweight and
natural way of saying a local variable could outlive the stack frame -- "@". :)
Long story short, I'm suggesting we could restrict lambdas to only be allowed
to refer to @-typed variables.
- Propagating typestate constraints to lambdas
When you use an explicitly declared helper function, you can propagate
typestate constraints to make a program type check:
fn helper(@mutable int x) : prime(x) { ... }
...
let @mutable int x = 17;
check prime(x);
... helper(x) ...
frobPrimeNumber(x);
If we turn that helper into a lambda with an upvar, we don't have a type
constraint to propagate the predicate:
let @mutable int x = 17;
check prime(x);
... lambda() { ... x = 12; ... } ...
frobPrimeNumber(x); // the lambda may have ruined prime-ness!
One approach would be to restrict upvars to be read-only. I *think* this would
be sound, since the helper wouldn't be able to violate the typestate of a
variable. And you could still get the mutable version using |bind|:
let @mutable int x = 17;
check prime(x);
... bind (lambda (@mutable int x) : prime(x) {
...
x = 12;
check prime(x);
...
})(x) ...
frobPrimeNumber(x);
In order to make that type-check, the programmer is forced to put the explicit
constraint on the parameter x.
But you could also take this a step further and just allow programmers to
specify constraints on a lambda's upvars, alleviating the need for the manual
closure conversion:
let @mutable int x = 17;
check prime(x);
... (lambda () : prime(x) {
...
x = 12;
check prime(x);
...
}) ...
frobPrimeNumber(x);
You can just view this as syntactic sugar for the one above. (BTW, I'm not
trying to propose exact concrete syntaxes, just looking for an existence proof
that it's possible to propagate typestate constraints into lambdas.)
That's just my current thinking, anyway. Graydon, does any of this sound
plausible?
Dave
On Aug 30, 2010, at 11:06 AM, Graydon Hoare wrote:
> On 10-08-28 02:33 PM, Sebastian Sylvan wrote:
>
>> Let me first say that the only reason I write this is because I really like
>> Rust. It seems to get a lot of things really right, and I'd like it to
>> become successful! With that said, here's one issue. I have more, and I may
>> write more about that later, but I figured I start with this one because it
>> actually ends with a suggestion!
>
> Hi,
>
> First, I'd like to remind you of the conduct guidelines for this community.
> While not quite venturing into the "abusive" or "flaming" category, for an
> introductory post/proposal your tone is needlessly provocative and filled
> with loaded terminology ("hijacking", "kludge", "severely crippled"). We aim
> for decorum here, please try to respect that. I had to rewrite my response a
> few times to ensure that I was not escalating the tone, and I don't enjoy
> spending work hours on such exercises.
>
> With respect to the technical feasibility of your proposal: it may be
> possible -- it's similar to how we currently translate foreach blocks, for
> example -- and I'll add a note about this proposal to the existing bug (issue
> #6) that's a work-item for a "lambda" short-form of anonymous function
> immediate. If anyone wants to take the time to work out how your proposal
> interacts with Rust semantics in more detail and ensure it'd be safe, I'd be
> happy to entertain that conversation. Such alias-based, scoped capture would
> have the pleasant additional benefit of being faster than function bindings
> (no heap allocation), so I'm sure nobody would object to trying to fit them
> in.
>
> Finally, I should clarify some misconceptions in your post. When I speak in
> the collective voice "we" here, I am referring to myself mainly as well as
> some opinions I believe to be shared with others who have worked on the Rust
> design. Please, others, speak up if you disagree:
>
> - Comprehensibility is an important design goal in Rust, so we're not
> terribly interested in appeals to "many" kinds of additional control
> flow abstraction. Control flow is a notorious comprehension hazard
> in code at the best of times. We may be interested in handling some
> well-motivated cases we currently do poorly (at the moment, for
> example, parallel iteration can't be done easily) as well as general
> abstractions that support >1 of those. Hypothetical use-cases, much
> less so.
>
> - Rust has very few nonlocal control mechanisms already. Catchable
> exceptions as outlined in your example are not something we consider
> particularly plausible in Rust, due to custom typestate predicates
> (there's a FAQ entry). Currently there is no such feature. Aside
> from termination-semantics failure and loop-break / loop-continue,
> there are no other nonlocal jumps, and (at the moment) not much
> interest expressed in adding any.
>
> - Destructors and RAII are not a "kludge", and try/finally blocks are
> not a particularly good replacement for them. Destructors and RAII
> encapsulate the initialization state of a resource and its
> sub-resources, such that only those resources acquired wind up being
> released. To mimic this in try/finally blocks is verbose and
> error-prone: you need a boolean "tracking variable" for each
> acquired resource that you set immediately after the resource is
> acquired, as well as logic at the finally-side to conditionally
> release only those resources acquired (and such logic is
> order-sensitive, and needs its own try/finally blocks in order to be
> as robust as a good destructor system). This is particularly
> error-prone since you only notice coding errors in such paths in the
> (rare) cases of exceptions actually being thrown; more likely, only
> non-throwing case is exercised and the errors are shipped
> un-noticed.
>
> In addition, destructors and RAII generalize to handle resources
> that outlive a control frame, without any change to the underlying
> resource-manager object. Our feeling is therefore that destructors
> and RAII are, on balance, a more-desirable feature than try/finally
> blocks would be (if we had them).
>
> If you have further concrete suggestions about changes to Rust, we'd be happy
> to hear some of them as well, but please try to keep the tone respectful and
> constructive. The (relatively interesting, reasonable) proposal in your post
> was almost lost amid the paragraphs of additional critique.
>
> Thanks,
>
> -Graydon
> _______________________________________________
> Rust-dev mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/rust-dev
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev