A follow-up to my previous email: there's one possible unsoundness depending on 
how our typestate works (betraying my ignorance, sorry). If it's possible to 
invalidate a typestate, then dealing with closures would have to be tightened 
so that whatever constraints a lambda requires of an upvar have to be upheld 
forever once the closure is created. Otherwise you could have, e.g.:

    let @mutable int x = 17;
    check prime(x);
    auto f = lambda() : prime(x) { frobPrimeNumber(x); };
    x = 12;
    f(); // crash

On a separate note, I agree that "down-lambdas" and "heap-lambdas" are two 
potentially different kinds of constructs with pretty different uses. There are 
lots of useful things about both. Down-lambdas are good for registering 
clean-up code for a scope, as we mentioned before, but they're also prevalent 
in a lot of functional patterns: map/forEach/fold, for example, all take 
down-lambda arguments. And a local recursive algorithm that is immediately 
applied is a very common functional pattern.

(But I do think arbitrary-lifetime lambdas are good for usability, since they 
come up so much in event-based programming. If they can be made to work, of 
course.)

Dave

On Aug 30, 2010, at 2:07 PM, David Herman wrote:

> 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

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

Reply via email to