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 use of pipe operator in patterns. #1882

Closed
wants to merge 3 commits into
base: master
from

Conversation

Projects
None yet
@Aaronepower

Aaronepower commented Feb 3, 2017

rendered

@petrochenkov

This comment has been minimized.

Show comment
Hide comment
@petrochenkov

petrochenkov Feb 3, 2017

Contributor

s/sub expressions/patterns/

Contributor

petrochenkov commented Feb 3, 2017

s/sub expressions/patterns/

@petrochenkov

This comment has been minimized.

Show comment
Hide comment
@petrochenkov

petrochenkov Feb 3, 2017

Contributor

I think you also have to introduce pattern grouping with parens () to disambiguate something like a @ PAT1 | PAT2.

Contributor

petrochenkov commented Feb 3, 2017

I think you also have to introduce pattern grouping with parens () to disambiguate something like a @ PAT1 | PAT2.

@Nemo157

This comment has been minimized.

Show comment
Hide comment
@Nemo157

Nemo157 Feb 3, 2017

Contributor

As an example this currently works:

match a {
    f @ 'b' | f @ 'c' => foo(f),
    _ => (),
}

and this currently fails because of f not being bound in the second pattern:

match a {
    f @ 'b' | 'c' => foo(f),
    _ => (),
}

but if 'b' | 'c' was itself a pattern then it could be possible to do something like

match a {
    f @ ('b' | 'c') => foo(f),
    _ => (),
}

to bind f to whatever matched the pattern 'b' | 'c' without having to bind in each sub-pattern individually

Contributor

Nemo157 commented Feb 3, 2017

As an example this currently works:

match a {
    f @ 'b' | f @ 'c' => foo(f),
    _ => (),
}

and this currently fails because of f not being bound in the second pattern:

match a {
    f @ 'b' | 'c' => foo(f),
    _ => (),
}

but if 'b' | 'c' was itself a pattern then it could be possible to do something like

match a {
    f @ ('b' | 'c') => foo(f),
    _ => (),
}

to bind f to whatever matched the pattern 'b' | 'c' without having to bind in each sub-pattern individually

@mbrubeck

This comment has been minimized.

Show comment
Hide comment
@mbrubeck

mbrubeck Feb 3, 2017

Contributor

I think a better way to modify the grammar might look something like this:

Remove the pats_or nonterminal, and replace it with pat in the match expression grammar:

nonblock_match_clause
: maybe_outer_attrs pat maybe_guard FAT_ARROW nonblock_expr
| maybe_outer_attrs pat maybe_guard FAT_ARROW full_block_expr;

block_match_clause
: maybe_outer_attrs pat maybe_guard FAT_ARROW block;

Change the top-level pat grammar to allow one or more patterns separated by |:

pat
: single_pat
| pat '|' single_pat;

And add a new single_pat rule that matches the old pat rule, with the possible addition of an alternation enclosed in parentheses:

single_pat
: UNDERSCORE
| '&' pat
| '&' MUT pat
// ...
| '(' pat '|' single_pat ')'
;

(There might be some ambiguity to resolve between that last rule and tuple patterns.)

Contributor

mbrubeck commented Feb 3, 2017

I think a better way to modify the grammar might look something like this:

Remove the pats_or nonterminal, and replace it with pat in the match expression grammar:

nonblock_match_clause
: maybe_outer_attrs pat maybe_guard FAT_ARROW nonblock_expr
| maybe_outer_attrs pat maybe_guard FAT_ARROW full_block_expr;

block_match_clause
: maybe_outer_attrs pat maybe_guard FAT_ARROW block;

Change the top-level pat grammar to allow one or more patterns separated by |:

pat
: single_pat
| pat '|' single_pat;

And add a new single_pat rule that matches the old pat rule, with the possible addition of an alternation enclosed in parentheses:

single_pat
: UNDERSCORE
| '&' pat
| '&' MUT pat
// ...
| '(' pat '|' single_pat ')'
;

(There might be some ambiguity to resolve between that last rule and tuple patterns.)

@Aaronepower Aaronepower changed the title from Allow use of pipe operator in sub expressions. to Allow use of pipe operator in patterns. Feb 3, 2017

@aturon aturon added the T-lang label Feb 3, 2017

@sgrif

This comment has been minimized.

Show comment
Hide comment
@sgrif

sgrif Feb 8, 2017

Contributor

I am in favor of this, but this was also proposed last year. Have the reasons that was postponed changed/been addressed?

(Note: WRT real world impact, I don't have links off-hand but I have run into wanting this 3-4 times in Diesel in the past 3 months or so. I'm sure I can dig up where I wanted this if needed)

Contributor

sgrif commented Feb 8, 2017

I am in favor of this, but this was also proposed last year. Have the reasons that was postponed changed/been addressed?

(Note: WRT real world impact, I don't have links off-hand but I have run into wanting this 3-4 times in Diesel in the past 3 months or so. I'm sure I can dig up where I wanted this if needed)

@Ericson2314

This comment has been minimized.

Show comment
Hide comment
@Ericson2314

Ericson2314 Feb 9, 2017

Contributor

Well, for one, the MIR is done now.

The Haskell community is discussing this right now, btw: ghc-proposals/ghc-proposals#43 (comment). http://gallium.inria.fr/%7Escherer/research/ambiguous_pattern_variables/ml_workshop_2016.abstract.pdf was especially useful and cited in that thread.

Contributor

Ericson2314 commented Feb 9, 2017

Well, for one, the MIR is done now.

The Haskell community is discussing this right now, btw: ghc-proposals/ghc-proposals#43 (comment). http://gallium.inria.fr/%7Escherer/research/ambiguous_pattern_variables/ml_workshop_2016.abstract.pdf was especially useful and cited in that thread.

@Aaronepower

This comment has been minimized.

Show comment
Hide comment
@Aaronepower

Aaronepower Feb 10, 2017

@petrochenkov @Nemo157 Why would the () be necessary? Wouldn't foo @ '\n' | '\r' => {} be ideal syntax?

@sgrif Well with the Rust roadmap, and I think this would fit well into lowering the learning curve for Rust, as it provides a much more intuitive syntax for pattern matching.

Aaronepower commented Feb 10, 2017

@petrochenkov @Nemo157 Why would the () be necessary? Wouldn't foo @ '\n' | '\r' => {} be ideal syntax?

@sgrif Well with the Rust roadmap, and I think this would fit well into lowering the learning curve for Rust, as it provides a much more intuitive syntax for pattern matching.

@petrochenkov

This comment has been minimized.

Show comment
Hide comment
@petrochenkov

petrochenkov Feb 10, 2017

Contributor

@Aaronepower
@Nemo157 gave an example of why introducing | as a high priority operator is a breaking change.

f @ 'b' | f @ 'c'

is currently a valid match arm and it's grouped as (f @ 'b') | (f @ 'c').
If | has high priority, then the example is parsed as an invalid pattern f @ ('b' | f @ 'c').

I've just grepped rustc and found a few examples in real code:

ref t @ TyUint(_) | ref t @ TyInt(_)
def @ None | def @ Some(Def::Local(_))
seq @ TokenTree::Delimited(..) | seq @ TokenTree::Token(_, DocComment(..))
// etc

&PAT | &PAT has this problem as well.

In general, it would be natural for | to have low priority, both for compatibility, and because binary/sequence operators have lower priority than unary operators in both expression and type grammars.

However, if | has low priority, then we cannot express foo @ ('\n' | '\r'), which can be useful. This can be fixed in the same way as it's fixed in expression and type grammars - with parens.

Contributor

petrochenkov commented Feb 10, 2017

@Aaronepower
@Nemo157 gave an example of why introducing | as a high priority operator is a breaking change.

f @ 'b' | f @ 'c'

is currently a valid match arm and it's grouped as (f @ 'b') | (f @ 'c').
If | has high priority, then the example is parsed as an invalid pattern f @ ('b' | f @ 'c').

I've just grepped rustc and found a few examples in real code:

ref t @ TyUint(_) | ref t @ TyInt(_)
def @ None | def @ Some(Def::Local(_))
seq @ TokenTree::Delimited(..) | seq @ TokenTree::Token(_, DocComment(..))
// etc

&PAT | &PAT has this problem as well.

In general, it would be natural for | to have low priority, both for compatibility, and because binary/sequence operators have lower priority than unary operators in both expression and type grammars.

However, if | has low priority, then we cannot express foo @ ('\n' | '\r'), which can be useful. This can be fixed in the same way as it's fixed in expression and type grammars - with parens.

@solson solson referenced this pull request Feb 11, 2017

Closed

Pattern synonyms #1895

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Apr 29, 2017

Member

This RFC is missing its motivation section, which is probably the most important section of an RFC!

While this feature seems potentially nice, there is often a lot of unanticipated detail work necessary to push changes like this through (e.g, @petrochenkov is already turning up some interesting issues). Before we spend time hashing out all those details, there needs to be a clear case for why this is important to prioritize right now.

Member

aturon commented Apr 29, 2017

This RFC is missing its motivation section, which is probably the most important section of an RFC!

While this feature seems potentially nice, there is often a lot of unanticipated detail work necessary to push changes like this through (e.g, @petrochenkov is already turning up some interesting issues). Before we spend time hashing out all those details, there needs to be a clear case for why this is important to prioritize right now.

@joshtriplett

This comment has been minimized.

Show comment
Hide comment
@joshtriplett

joshtriplett May 11, 2017

Member

One corner case in treating this as pure syntactic sugar: what if I write this:

match a_big_tuple {
(0|1, 0|1, 0|1, 0|1, 0|1, 0|1, ...) => ...

(Or anything else where you embed multiple alternations inside a pattern.) The obvious syntactic-sugar expansion would turn that into 2^n patterns. (The compiler ought to be able to optimize that in cases like this, but it could result in a large intermediate state if not handled specially.)

Does this seem like a problem?

Member

joshtriplett commented May 11, 2017

One corner case in treating this as pure syntactic sugar: what if I write this:

match a_big_tuple {
(0|1, 0|1, 0|1, 0|1, 0|1, 0|1, ...) => ...

(Or anything else where you embed multiple alternations inside a pattern.) The obvious syntactic-sugar expansion would turn that into 2^n patterns. (The compiler ought to be able to optimize that in cases like this, but it could result in a large intermediate state if not handled specially.)

Does this seem like a problem?

@mglagla

This comment has been minimized.

Show comment
Hide comment
@mglagla

mglagla May 22, 2017

Small data point: i am making a small tetris clone using the sdl2-crate. In my code i have a match expression which looks like this, if i understand it right:

for event in event_pump.poll_iter() {
    use sdl2::event::Event;
    use sdl2::keyboard::Keycode::{Escape, Q};
    match event {
        Event::KeyDown {
            keycode: Some(Escape),
            ..
        }
        | Event::KeyDown {
            keycode: Some(Q),
            ..
        } => break 'game,
        _ => {},
    }

With this RFC i would be able to express more concisely like this:

for event in event_pump.poll_iter() {
    use sdl2::event::Event;
    use sdl2::keyboard::Keycode::{Escape, Q};
    match event {
        Event::KeyDown {
            keycode: Some(Escape | Q),
            ..
        } => break 'game,
        _ => {},
    }

In my opinion, this is more clear and thus less error-prone.

mglagla commented May 22, 2017

Small data point: i am making a small tetris clone using the sdl2-crate. In my code i have a match expression which looks like this, if i understand it right:

for event in event_pump.poll_iter() {
    use sdl2::event::Event;
    use sdl2::keyboard::Keycode::{Escape, Q};
    match event {
        Event::KeyDown {
            keycode: Some(Escape),
            ..
        }
        | Event::KeyDown {
            keycode: Some(Q),
            ..
        } => break 'game,
        _ => {},
    }

With this RFC i would be able to express more concisely like this:

for event in event_pump.poll_iter() {
    use sdl2::event::Event;
    use sdl2::keyboard::Keycode::{Escape, Q};
    match event {
        Event::KeyDown {
            keycode: Some(Escape | Q),
            ..
        } => break 'game,
        _ => {},
    }

In my opinion, this is more clear and thus less error-prone.

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Jul 25, 2017

Member

I'm going to move to close this RFC as-is; the basic issues around the RFC content remain. While the lang team is potentially open to an enhancement along these lines, we need a full-blown RFC before proceeding.

@rfcbot fcp close

Member

aturon commented Jul 25, 2017

I'm going to move to close this RFC as-is; the basic issues around the RFC content remain. While the lang team is potentially open to an enhancement along these lines, we need a full-blown RFC before proceeding.

@rfcbot fcp close

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Jul 25, 2017

Team member @aturon has proposed to close this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

rfcbot commented Jul 25, 2017

Team member @aturon has proposed to close this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@joshtriplett

This comment has been minimized.

Show comment
Hide comment
@joshtriplett

joshtriplett Jul 26, 2017

Member

@Aaronepower To further clarify: if you're interested in seeing this go into Rust, that's still possible; it just needs further expansion into a full RFC that addresses all the issues raised in this thread.

I'm personally interested in seeing this.

Member

joshtriplett commented Jul 26, 2017

@Aaronepower To further clarify: if you're interested in seeing this go into Rust, that's still possible; it just needs further expansion into a full RFC that addresses all the issues raised in this thread.

I'm personally interested in seeing this.

@liigo

This comment has been minimized.

Show comment
Hide comment
@liigo

liigo Jul 28, 2017

Contributor

I'm confused. What's the type of expression a | b? and what is its value?

Contributor

liigo commented Jul 28, 2017

I'm confused. What's the type of expression a | b? and what is its value?

@eddyb

This comment has been minimized.

Show comment
Hide comment
@eddyb

eddyb Jul 28, 2017

Member

@liigo It's not an expression, it's a pattern for "try pattern a and if that doesn't match try b".

Member

eddyb commented Jul 28, 2017

@liigo It's not an expression, it's a pattern for "try pattern a and if that doesn't match try b".

@nrc

This comment has been minimized.

Show comment
Hide comment
@nrc

nrc Jul 31, 2017

Member

Agree we should close. Although I do think it would be a good feature to have, I don't think it should be high priority right now.

Member

nrc commented Jul 31, 2017

Agree we should close. Although I do think it would be a good feature to have, I don't think it should be high priority right now.

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Aug 9, 2017

Member

(Checking off for @pnkfelix, who is away)

Member

aturon commented Aug 9, 2017

(Checking off for @pnkfelix, who is away)

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Aug 9, 2017

🔔 This is now entering its final comment period, as per the review above. 🔔

rfcbot commented Aug 9, 2017

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Aug 19, 2017

The final comment period is now complete.

rfcbot commented Aug 19, 2017

The final comment period is now complete.

@liigo

This comment has been minimized.

Show comment
Hide comment
@liigo

liigo Aug 22, 2017

Contributor

It's not an expression, it's a pattern for "try pattern a and if that doesn't match try b".

According to the refference:

The type of the patterns must equal the type of the head expression.

the pattern Some('a' | 'b')'s type must be Option(char). so the type of 'a' | 'b' should be char?

Contributor

liigo commented Aug 22, 2017

It's not an expression, it's a pattern for "try pattern a and if that doesn't match try b".

According to the refference:

The type of the patterns must equal the type of the head expression.

the pattern Some('a' | 'b')'s type must be Option(char). so the type of 'a' | 'b' should be char?

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Aug 24, 2017

Contributor

@nrc I disagree - it helps ergonomics a lot and should fit in well with the 2017 ergonomics initiative.

Contributor

Centril commented Aug 24, 2017

@nrc I disagree - it helps ergonomics a lot and should fit in well with the 2017 ergonomics initiative.

@liigo

This comment has been minimized.

Show comment
Hide comment
@liigo

liigo Aug 26, 2017

Contributor

I agree it helps ergonomics, but a little.

Contributor

liigo commented Aug 26, 2017

I agree it helps ergonomics, but a little.

@aturon

This comment has been minimized.

Show comment
Hide comment
@aturon

aturon Aug 26, 2017

Member

Closing, as per FCP. Thanks @Aaronepower for the RFC!

Member

aturon commented Aug 26, 2017

Closing, as per FCP. Thanks @Aaronepower for the RFC!

@mbrubeck

This comment has been minimized.

Show comment
Hide comment
@mbrubeck

mbrubeck Sep 26, 2018

Contributor

New RFC for this feature: #2535.

Contributor

mbrubeck commented Sep 26, 2018

New RFC for this feature: #2535.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment