Hi everyone,

Here's a revised lambda syntax tweak proposal. It's gotten feedback from several, so I think it's time to present it more generally.

I don't think we should do this now; we should remain focused on bugs for 0.3. I'm just interested in getting feedback.

Executive summary of the proposal:

1. Change the block lambda syntax from { |x| x + 3 } to |x| x + 3.

2. In general, remove the special case of "pulling the last argument out and turning it into a block" from the language. However, it is still supported, and in fact required, as part of the "for" construct and the new "do" construct.

3. Change this:

    for [ 1, 2, 3 ].map { |x|
        ...
    }

To this:

    for [ 1, 2, 3 ].map |x| {
        ...
    }

4. Add a new form, `do`, in order to support block syntax for constructs that aren't strictly loops. Also remove the mandatory || for zero-argument lambda blocks. Thus instead of:

    spawn { ||
        ...
    }

We have:

    do spawn {
        ...
    }

Rationale:

* Removing the ability for the last argument of a function call to be pulled out in the general case significantly decreases the complexity of the language grammar. This feature simply becomes a mandatory part of the "for" or "do" expressions.

* Removing the pipes around zero-argument functions in "do" or "for" constructs improves readability. In particular "do spawn" reads naturally.

* Having the opening brace be at the end of the line increases familiarity for programmers accustomed to the C language family.

* Removing the mandatory braces around lambdas decreases visual noise for small lambda expressions. Consider the closing parentheses on this line:

    log([ 1, 2, 3 ].map(|x| x + 1))

Versus the sequence '}))' on this line:

    log([ 1, 2, 3 ].map({ |x| x + 1 }))

* Having only one way to get the block syntax iteration ("for" or "do") provides an incentive for library authors to ensure that their blocks follow the iteration protocol. At the same time, it minimizes surprises when people try to put "break"/"continue"/"ret" in blocks intended for iteration and discover it doesn't work. Now it always works for iterators.

* The "do" block allows "continue" for early returns, which is something that cannot be done at the moment with block lambdas.

Here are the technical details:

Pseudo-BNF:

    PrimaryExpr ::== ... | BlockLambda | ForExpr | DoExpr

    BlockLambda ::== AbbreviatedArgs Expr

    AbbreviatedArgs ::== '||' | '|' (ModeSigil Identifier)* '|'

    ForExpr ::== 'for' (CallExpr | PrimaryExpr) AbbreviatedArgs? Block

    DoExpr ::== 'do' (CallExpr | PrimaryExpr) AbbreviatedArgs? Block

If the "for" expression or "do" expression head is a primary expression that is not a call expression, then it's treated as a call expression with an empty argument list.

The block lambda in a "for" expression is treated as returning bool, while the block lambda in a "do" expression is treated as returning unit. As such, "break"/"continue"/"ret" all work in "for" loops, while only "continue" works in "do" expressions (which effects an early return).

Thoughts?

Patrick
_______________________________________________
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to