Skip to content
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

Allow an optional vert at the beginning of a match #1745

Closed
mdinger opened this issue Sep 9, 2016 · 10 comments
Closed

Allow an optional vert at the beginning of a match #1745

mdinger opened this issue Sep 9, 2016 · 10 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@mdinger
Copy link
Contributor

mdinger commented Sep 9, 2016

This was originally proposed in rust-lang/rust#36361


See how in the following, all the | bars are mostly aligned except the last one:

#![allow(dead_code)]

use E::*;

enum E {
    A,
    B,
    C,
    D,
}

fn main() {
    match A {
        A |
        B |
        C |
        D => (),
    }
}

I'd propose it be allowed at the beginning of the pattern as well enabling something like this:

#![allow(dead_code)]

use E::*;

enum E {
    A,
    B,
    C,
    D,
}

fn main() {
    match A {
    | A
    | B
    | C
    | D => (),
    }
}

This appears to be the official style for F# matches and it has grown on me a lot. It highlights the matches and doesn't require as much deeper nesting. After getting used to the F# style, the inability to do this is rust seems a bit limiting.

@mdinger
Copy link
Contributor Author

mdinger commented Sep 9, 2016

@Victor-Savu I'm not sure if you understood the proposal properly or not. Your example made sense to me:

enum E {
    A,
    B,
    C,
    D,
}

// These are all equivalent
fn main() {
    match A {
        A | B => println!("Give me A | B!"),
        C | D => println!("Why am I here?"),
    }

    match A {
    | A | B => println!("Give me A | B!"),
    | C | D => println!("Why am I here?"),
    }

    match A {
    | A
    | B => println!("Give me A | B!"),
    | C
    | D => println!("Why am I here?"),
    }

    match A {
        A | B =>
            println!("Give me A | B!"),
        C | D =>
            println!("Why am I here?"),
    }
}

Making both syntax legal could make some slightly weird cases such as @KalitaAlexey's example where both are used:

enum E { A, B, C }

fn main() {
    use E::*;
    let value = A;
    match value {
    | A
    | B => {},
    C => {}
//  ^ Could be interpreted as slightly inconsistent. Nevertheless, it doesn't
// seem like something to be concerned about.
    }
}

@Victor-Savu Suffice to say, when dealing with very large nested enumerations, this type of syntax is very valuable by limiting rightward drift more than rust code would. It also allows the same consistent grouping of subsets of the enumeration as before. This is particularly valuable when types are distinct but conceptually similar and may as a group follow the same branch.

@mdinger
Copy link
Contributor Author

mdinger commented Sep 10, 2016

@Victor-Savu Here's a slightly more complete example. Note that the context of the example should make the matching fairly clear even if brace positioning is tricky to align properly. Also, the further the nesting goes, the worse the rightward drift, making this proposal a potentially cleaner alternative:

struct FavoriteBook {
    author: &'static str,
    title: &'static str,
    date: u64
}

// Full name and surname. 
enum Franks { Alice, Brenda, Charles, Dave, Steve }
enum Sawyer { Tom, Sid, May }

enum Name {
    Franks(Franks),
    Sawyer(Sawyer),
}

fn main() {
    let name = Name::Sawyer(Sawyer::Tom);

    // Here is the first match in a typical rust style    
    match name {
        Name::Franks(name) =>
            match name {
                Franks::Alice |
                Franks::Brenda |
                Franks::Dave => FavoriteBook {
                        author: "alice berkley",
                        title: "Name of a popular book",
                        date: 1982,
                    },
                Franks::Charles |
                Franks::Steve => FavoriteBook {
                        author: "fred marko",
                        title: "We'll use a different name here",
                        date: 1960,
                    },
        },
        Name::Sawyer(name) =>
            match name {
                Sawyer::Tom => FavoriteBook {
                        author: "another name",
                        title: "Again we change it",
                        date: 1999,
                    },
                Sawyer::Sid |
                Sawyer::May => FavoriteBook {
                        author: "again another name",
                        title: "here is a different title",
                        date: 1972,
                    },
        },
    };

    // An alternate rust style might look something like this:
    match name {
    | Name::Franks(name) =>
        match name {
        | Franks::Alice
        | Franks::Brenda
        | Franks::Dave => FavoriteBook {
                author: "alice berkley",
                title: "Name of a popular book",
                date: 1982
            }
        | Franks::Charles
        | Franks::Steve => FavoriteBook {
                author: "fred marko",
                title: "We'll use a different name here",
                date: 1960
            }
        }
    | Name::Sawyer(name) =>
        match name {
        | Sawyer::Tom => FavoriteBook {
                author: "another name",
                title: "Again we change it",
                date: 1999
            }
        | Sawyer::Sid
        | Sawyer::May => FavoriteBook {
                author: "again another name",
                title: "here is a different title",
                date: 1972
            }
        }
    };
}

Laying the matches out in that fashion interacts with braces and commas in weird ways. This isn't an issue in F# because indentation dictates logic. In rust, any time you don't indent but create new braces, any inner braces closing points will be the same as the outer braces.

There are things that could be done to alleviate issues such as this but it might require a drastic stylistic change in certain areas (such as eliding braces and semicolons under certain circumstances) which I don't think very likely. Even if was likely, there would probably have to be a strong consensus for such a thing. At this point, that does not exist so I would not suggest it.

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Sep 10, 2016
@Victor-Savu
Copy link

@mdinger Thank you for taking the time to write the extra examples and I see now that the main focus is the right-drift. That is a valid point and I think it is a good issue to tackle.

@joshtriplett
Copy link
Member

I like this idea. It seems to have an effect similar to trailing commas, in making expressions more uniform.

@Ericson2314
Copy link
Contributor

(I'd love to allow leading commas on every line too!)

@joshtriplett
Copy link
Member

Nominating this; I think it's a simple, straightforward change, with a significant usability and consistency improvement.

@joshtriplett
Copy link
Member

General consensus in the @rust-lang/lang team meeting was in favor of this change. If it had an associated RFC written, the inclination was to accept. @mdinger, would you like to turn this into a pull request with an RFC?

@mdinger
Copy link
Contributor Author

mdinger commented Feb 24, 2017

@joshtriplett Done. Thanks for spending the time to examine it with them. I tried to include what I could in the RFC. I'm sure it will probably require modifications but hopefully it is at least decent.

@dtolnay
Copy link
Member

dtolnay commented Sep 17, 2017

This RFC was accepted in #1925. The tracking issue is rust-lang/rust#44101.

@swfsql
Copy link

swfsql commented May 31, 2018

sorry if this is the wrong place to post this:
This reminded me of some TLA+ syntax, which I like: https://youtu.be/4snwZl726c4?t=8m50s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

8 participants