Skip to content

RFC: Small closure syntax tweak #2394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pcwalton opened this issue May 17, 2012 · 16 comments
Closed

RFC: Small closure syntax tweak #2394

pcwalton opened this issue May 17, 2012 · 16 comments

Comments

@pcwalton
Copy link
Contributor

Might as well submit this as an RFC. I propose this syntactic tweak:

Expr ::== ... | Call | BlockLambda
BlockLambda ::== '|' AbbreviatedArgs '|' '{' (Stmt ';')* Expr '}'
Call ::== Primary '(' (Expr (',' Expr)*)? ')' (':' BlockLambda)?

In other words, keep the closure syntax exactly the same, but move the sticks before the initial curly brace, and add a colon before the sticks in the Ruby-like call notation.

The reason is to keep the brace at the end of the line:

for [ 1, 2, 3 ].each: |x| {
    io::println(#fmt(x))
}

As opposed to (currently):

for [ 1, 2, 3 ].each { |x|
    io::println(#fmt(x))
}

The former looks more familiar to C programmers (and Ruby/Smalltalk programmers) and looks nicer to me. The reason is that the brace is at the end of the line, allowing the eye to follow control the same way the user does in C.

The other nice things about this syntax are forward-thinking: it might allow us to drop the braces in the future, and it might allow us to use the Primary '{' ... '}' syntactic space for other things.

@catamorphism
Copy link
Contributor

I like the current syntax a little better, but I don't have a particularly good reason, thus I won't stand in the way.

@brson
Copy link
Contributor

brson commented May 18, 2012

Some more examples:

task::unkillable: || {
   ...
}

vs.

task::unkillable { ||
    ...
}
vec::as_buf(ports): |ports| {
}

vs.

vec::as_buf(ports) { |ports|
}
    iter::repeat(times): || {
        task::spawn: || {
            iter::repeat(msgs): || {
                send(ch_a, "a")
            }
        };
        task::spawn: || {
            iter::repeat(msgs): || {
                send(ch_b, "b")
            }
        };
    }

vs.

    iter::repeat(times) {||
        task::spawn {||
            iter::repeat(msgs) {||
                send(ch_a, "a")
            }
        };
        task::spawn {||
            iter::repeat(msgs) {||
                send(ch_b, "b")
            }
        };
    }
    let result = result::chain(result): |config| {
        let output_format = getopts::opt_maybe_str(
            match, opt_output_format());
        option::map_default(output_format, result::ok(config)): |output_format| {
            result::chain(parse_output_format(output_format)): |output_format| {
                result::ok({
                    output_format: output_format
                    with config
                })
            }
        }
    };

vs.

    let result = result::chain(result) {|config|
        let output_format = getopts::opt_maybe_str(
            match, opt_output_format());
        option::map_default(output_format, result::ok(config)) {|output_format|
            result::chain(parse_output_format(output_format)) {|output_format|
                result::ok({
                    output_format: output_format
                    with config
                })
            }
        }
    };

@brson
Copy link
Contributor

brson commented May 18, 2012

Also

let f: fn@(int) -> int = |i| { i + 1 };

let f: fn@(int) -> int = { |i| i + 1 };

@brson
Copy link
Contributor

brson commented May 18, 2012

I'm ambivilant. I guess I slightly prefer the proposed syntax.

@nikomatsakis
Copy link
Contributor

No strong opinion. I like that it makes this possible: vec.map(|x| x+1).

@pcwalton but why do you say your proposal is more familiar to Ruby programmers? As far as I can tell, what we have now is the Ruby syntax.

@pcwalton
Copy link
Contributor Author

Yes, my mistake: what we have now is the Ruby syntax. Ruby house style tends to prefer paired do/end keywords for blocks that span more than a single line, though.

@Dretch
Copy link
Contributor

Dretch commented May 19, 2012

If I understand correctly then the : in the new syntax makes the difference between a regular lambda and a last-argument-to-call lambda greater than in the current syntax, i.e:

Current syntax: vec.map({|x| x+1}) vs vec.map {|x| x+1}

Proposed syntax: vec.map(|x| {x + 1}) vs vec.map: |x| {x + 1}

I therefore suggest that this proposal increases syntactic complexity.

@pcwalton
Copy link
Contributor Author

I actually think that it makes the syntax simpler to understand: the colon indicates to the eye that there's something special going on. It suggests a connection between the lambda and the call before it. With simple juxtaposition, however, the lambda looks separate from the call; it's not immediately obvious that they're connected.

@nikomatsakis
Copy link
Contributor

@pcwalton does the trailing : unambiguously signify a closure? That is, would we be able to write:

task::spawn: {
    ...
}

@pcwalton
Copy link
Contributor Author

I wish, but that makes stuff like { spawn: { print("foo"); } } ambiguous. But we could have something like spawn: -> { ... }

@nikomatsakis
Copy link
Contributor

@pcwalton ah yes of course. It's always record literals. I prefer || { ... } to -> { ... }, more consistent.

@pcwalton
Copy link
Contributor Author

|| actually reads terribly ugly to me as a way of denoting a lambda, but that's a separate proposal.

@pcwalton
Copy link
Contributor Author

Another thing came to mind when looking at this page: Perl 6's syntax works for us. That's -> args block.

Benefits of this syntax over the current syntax are: (a) one fewer shifted character; (b) keeps braces at the end of the line, like C; (c) avoids the argument list entirely for the zero-argument case; (d) makes function declarations visually resemble their types.

Benefits of this syntax over the proposed syntax are: (a) one fewer shifted character, and two for function calls; (b) avoids the colon after the function for Ruby block syntax; (c) avoids the argument list entirely for the zero-argument case; (d) makes function declarations visibly resemble their types.

Benefits of this syntax over the earlier "no-sticks" proposal I sent to the mailing list are (a) no colon required for the Ruby block syntax; (b) the arrow draws the eye toward the argument list, making it stand out; (c) makes function declarations visibly resemble their types.

Examples:

for [ 1, 2, 3 ].each -> x {
    io::println(#fmt(x))
}

vs.

for [ 1, 2, 3 ].each { |x|
    io::println(#fmt(x))
}
task::unkillable -> {
   ...
}

vs.

task::unkillable { ||
    ...
}
vec::as_buf(ports) -> ports {
}

vs.

vec::as_buf(ports) { |ports|
}
    iter::repeat(times) -> {
        task::spawn -> {
            iter::repeat(msgs) -> {
                send(ch_a, "a")
            }
        };
        task::spawn -> {
            iter::repeat(msgs) -> {
                send(ch_b, "b")
            }
        };
    }

vs.

    iter::repeat(times) {||
        task::spawn {||
            iter::repeat(msgs) {||
                send(ch_a, "a")
            }
        };
        task::spawn {||
            iter::repeat(msgs) {||
                send(ch_b, "b")
            }
        };
    }
    let result = result::chain(result) -> config {
        let output_format = getopts::opt_maybe_str(
            match, opt_output_format());
        option::map_default(output_format, result::ok(config)) -> output_format {
            result::chain(parse_output_format(output_format)) -> output_format {
                result::ok({
                    output_format: output_format
                    with config
                })
            }
        }
    };

vs.

    let result = result::chain(result) {|config|
        let output_format = getopts::opt_maybe_str(
            match, opt_output_format());
        option::map_default(output_format, result::ok(config)) {|output_format|
            result::chain(parse_output_format(output_format)) {|output_format|
                result::ok({
                    output_format: output_format
                    with config
                })
            }
        }
    };

let f: fn@(int) -> int = -> i, j { i + j };

vs.

let f: fn@(int) -> int = { |i, j| i + j };

@kud1ing
Copy link

kud1ing commented May 21, 2012

I think the colon increases the mental burden.

let max: uint;

vs

vec.map: |x| {x + 1};

Currently a colon mostly seperates the type from the variable. With the current proposal one has to add a mental check to many occurances of a colon while reading code "is this a variable declaration or a passing of arguments?"

That's also why i am against the ->proposal:

let f: fn@(int) -> int = -> i, j { i + j };

@pcwalton
Copy link
Contributor Author

Keep in mind that colons are also used in record literals: { a: 1, b: 2, c: 3 }.

Also, I agree that the let f: ... example is ugly, but it's also rather rare; you usually don't assign block lambdas to type-annotated variables that way. Besides, it's really an instance of declaration-follows-use; in C-like languages, the type grammar is explicitly designed to follow the corresponding expression grammar. It doesn't seem worse to me than this:

let x = 3;
let y: [int] = [x];`

@pcwalton
Copy link
Contributor Author

Closing this because I think we have something better as discussed yesterday.

oli-obk pushed a commit to oli-obk/rust that referenced this issue Apr 2, 2020
useless Rc<Rc<T>>, Rc<Box<T>>, Rc<&T>, Box<&T>

refers to  rust-lang#2394

changelog: Add lints for Rc<Rc<T>> and Rc<Box<T>> and Rc<&T>, Box<&T>

this is based on top of another change rust-lang#5310 so probably should go after that one.
bors added a commit to rust-lang-ci/rust that referenced this issue Sep 22, 2022
Fix bugs where unique_range became invalid

And also expand the cache integrity checks to cover this case.

I'm going to run this over all the ICEs I've gotten out of Miri recently, could be a bit.

Fixes rust-lang/miri#2389
tshepang pushed a commit to tshepang/rust that referenced this issue May 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants