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

Pattern synonyms #1895

Closed
wants to merge 2 commits into from
Closed

Conversation

hadronized
Copy link
Contributor

@hadronized hadronized commented Feb 10, 2017

Introduce pattern synonyms, used to create new patterns we can pattern match against from real patterns (enum’s variants, struct destructuring, etc.).

@kennytm
Copy link
Member

kennytm commented Feb 10, 2017

What's wrong with macros?

#![feature(slice_patterns)]

type Point = i32;

#[derive(Debug)]
enum Face<'a> {
    Point(&'a Point),
    Line(&'a Point, &'a Point),
    Polygon(&'a Point, &'a Point, &'a Point, &'a [&'a Point]),
} 

macro_rules! triangle {
    ($a:pat, $b:pat, $c:pat) => { Face::Polygon($a, $b, $c, &[]) }
}

macro_rules! quadrilateral {
    ($a:pat, $b:pat, $c:pat, $d:pat) => { Face::Polygon($a, $b, $c, &[$d]) }
}

fn print_face(f: &Face) {
    match *f {
        triangle!(a, b, c) => { println!("triangle [{}, {}, {}]", *a, *b, *c); }
        quadrilateral!(a, b, c, d) => { println!("quadrilateral [{}, {}, {}, {}]", *a, *b, *c, *d); }
        _ => { println!("other stuff ({:?})", *f); }
    }
}

fn main() {
    print_face(&Face::Point(&1));
    print_face(&Face::Line(&1, &2));
    print_face(&Face::Polygon(&1, &2, &3, &[]));
    print_face(&Face::Polygon(&1, &2, &3, &[&4]));
    print_face(&Face::Polygon(&1, &2, &3, &[&4, &5]));
}

@aturon aturon added the T-lang Relevant to the language team, which will review and decide on the RFC. label Feb 10, 2017
@aturon
Copy link
Member

aturon commented Feb 10, 2017

Just a quick note: this proposal seems related to (but narrow than) Scala's "extractor objects". See:

@nikomatsakis and I have talked over the years about wanting something like extractors for Rust, but it's never seemed like a very high priority. I suspect that if we wanted to support something like this RFC, we'd probably want to go the full way to extractors to get broader benefits.

@aturon aturon self-assigned this Feb 10, 2017
@hadronized
Copy link
Contributor Author

hadronized commented Feb 10, 2017

@kennytm you can do it with macros, yeah. But it’s still macros, you don’t have the unification with the pattern destructing style. If you have an error, you’ll get macro stuff in the error messages.

@aturon actually, I miss Haskell’s pattern synonyms – the motivation is about that. I didn’t know about extractor objects. I’ll have a look!

EDIT: Ok, I read it, and it’s interesting, indeed!

@mark-i-m
Copy link
Member

Rendered

/// A triangle.
pattern Triangle(a, b, c) => Polygon(a, b, c, &[])
/// A quadrangle.
pattern Quadrangle(a, b, c, d) => Polygon(a, b, c, &[d])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon on this line and on the pattern definition above.

@mglagla
Copy link
Contributor

mglagla commented Feb 10, 2017

This RFC should probably address backwards compatibility issues due to introducing a new keyword pattern.

One possible way is to make it contextual like union.

@solson
Copy link
Member

solson commented Feb 10, 2017

But it’s still macros, you don’t have the unification with the pattern destructing style.

@phaazon Can you elaborate? I'm not sure what this objection means.

If you have an error, you’ll get macro stuff in the error messages.

This doesn't seem like a huge deal. We've accepted this cost in other areas to avoid extending the language.

@withoutboats
Copy link
Contributor

A sort of obvious extension to this RFC would be to have "pattern synonyms" in trait bodies, so you can destructure generics types. I suspect this is similar to the concept of 'extractors' in Scala.

On that front, this concept seems quite related to the 'fields in traits' RFC #1546

@hadronized
Copy link
Contributor Author

@solson A macro is opaque. It would make the match block looking a bit weird, I guess:

match face {
  Point(x) => {},
  triangle!(x,y z) => {},

@nikomatsakis
Copy link
Contributor

Something that has always concerned me with scala's extractor objects is that they lose the ability to check exhaustiveness. I consider guaranteeing exhaustiveness to be a key attribute of Rust's match. It seems to me that pattern synonyms -- or something more transparent -- could be an improvement in that respect, though it's sad that you lose the ability to do things like use regular expressions as a pattern.

In any case, my overall inclination is to postpone this RFC. My feeling is that making this sort of change doesn't strike me as a high priority right now -- I don't feel it fits into the roadmap priorities in any particular way, nor do I feel that it is a kind of "key capability" for unlocking some set of applications, though it definitely makes some things nicer.

I'm a bit torn about some other aspects of the RFC. On the one hand, I appreciate it's "min-max" character -- focusing on inherent impls and "pattern aliases" means that this feels like a small extension to the language, which is good. On the other hand, that also means that some of the potential use cases that more general alternatives offer are not covered (e.g., using a regular expression as a pattern, or perhaps unifying with the trait system to make a more "enum-like" trait). I'd sort of like to have an overall vision of the ultimate extents of this feature, even if we don't want to talk all those steps at once.

I think it might be good to identify other use cases besides "convenience" and the rather abstract triangle example (even if they aren't fully elaborated). As a simple example, Scala's extractor objects are often cited as a way to migrate away from an enum (well, a case class) into a different internal representation without breaking backwards compatibility. And I already mentioned the idea of using a regex value as a "pattern" as well as somehow bringing enums/traits a bit closer together. I'm not sure what else is out there.

@nikomatsakis
Copy link
Contributor

One last thought -- we've struggled a bit with the meaning of constants in match (basically, are they a "pattern synonym" for the value, or do they compare as a kind of value equality). This isn't directly intersecting this RFC but it feels related.

@solson
Copy link
Member

solson commented Feb 11, 2017

@phaazon That macro example is no more opaque than pattern aliases. Actually, maybe less?

match a {
  Bar(x) => {},
  Foo(x, 2) => {},
}

If we had pattern aliases, these patterns might expand to the same thing or overlap in some way, but they just look like regular variants. At least with foo!(x, 2) you have a marker telling you something non-regular is going on.

@solson
Copy link
Member

solson commented Feb 11, 2017

One concrete case I've run into is stuff like this in rustc:

            ty::TyRef(_, ty) | ty::TyRawPtr(ty) | ty::TyBox(ty) => {},

Which is repeated in many matches. (Although TyBox is kind of gone now.)

But that would further require #1882 to make a pattern alias, and I'm not sure if a macro can expand to a match pattern with pipes right now, either.

@Ixrec
Copy link
Contributor

Ixrec commented Feb 11, 2017

I'm not sure if a macro can expand to a match pattern with pipes right now, either.

https://is.gd/lvSpQP produces "error: macro expansion ignores token | and any following", so it seems impossible right now.


I also feel the exhaustiveness of match blocks is an important feature, and unlike macros, extractors don't seem to have much application outside of pattern matching, so imo the motivation of this proposal would be better addressed by various improvements to macros (which are already an okay solution for this). These are examples we probably want to keep in mind for pattern/by example/declarative macros 2.0.

@hadronized
Copy link
Contributor Author

hadronized commented Feb 11, 2017

If we had pattern aliases, these patterns might expand to the same thing or overlap in some way […] with foo!(x, 2) you have a marker telling you something non-regular is going on.

I disagree with that. You shouldn’t be able to notice the difference between an alias and the type it’s aliased from. Besides declaration, you don’t have a different syntax to treat a type and a struct or even an enum. There’s nothing non-regular about pattern aliases, because they’re just replaced by the pattern they’re aliased from.

match a {
  Bar(x) => {},
  Foo(x, 2) => {},
}

If Bar is a pattern alias defined by:

  pattern Bar(x) => Foo(x, 2);

Then it’s isomorphic to:

match a {
  Foo(x, 2) => {},
  Foo(x, 2) => {},
}

The compiler should just give you the information that Bar is a pattern synonym to Foo(x, 2) and that you’re duplicating patterns. If you use bar!(x), you force people to understand that bar is a macro-powered pattern. That makes it unclear what that pattern-like macro does. Plus, you add the possibility to make an error while implementing the macro (or you could even do something nastier under the hood). I don’t like the macro appproach, it’s way too opaque. At least, a pattern synonym is just a mapping (hence the =>).

@aturon
Copy link
Member

aturon commented Feb 12, 2017

@nikomatsakis

In any case, my overall inclination is to postpone this RFC. My feeling is that making this sort of change doesn't strike me as a high priority right now -- I don't feel it fits into the roadmap priorities in any particular way, nor do I feel that it is a kind of "key capability" for unlocking some set of applications, though it definitely makes some things nicer.

I tend to agree. Despite my affection for Scala's extractor objects, we have much bigger fish to fry in the language right now, and the design space around this feature is non-trivial (as you point out). So while I think it'd be fine to keep exploring the space on an internals thread, I think the time isn't right for landing such an RFC.

@phaazon or others in support: is there a strong argument from your perspective that such a feature supports the 2017 roadmap?

@hadronized
Copy link
Contributor Author

As I find that feature an extra one, I guess it doesn’t fit the 2017 roadmap. I don’t see anything else than convenience here, but I might be wrong. I already used such a feature in a Haskell project I maintain (see this page). And as you can see, without the pattern synonyms, you can still pattern match on Face. So to me, it’s just convenience – but I like it very much, and people using my package told me it’s very nice to have them.

@aturon
Copy link
Member

aturon commented Feb 13, 2017

@phaazon Yes, I totally agree that a feature along these lines could be a nice convenience in some cases. It's just a question of how we spend our design bandwidth -- since if we consider such a feature, we'd probably also want to explore extractor objects etc. I think that right now we have bigger convenience/ergonomics gains to focus on, so we should revisit this later on. (You might leave a comment/link on rust-lang/rust-roadmap-2017#17 for tracking purposes.)

As such, I'm formally proposing to postpone this RFC.

@rfcbot fcp postpone

@rfcbot
Copy link
Collaborator

rfcbot commented Feb 13, 2017

Team member @aturon has proposed to postpone 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.

@hadronized hadronized changed the title Added pattern_synonyms RFC. Pattern synonyms Feb 15, 2017
@rfcbot
Copy link
Collaborator

rfcbot commented Feb 23, 2017

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

@rfcbot rfcbot added the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Feb 23, 2017
@rfcbot
Copy link
Collaborator

rfcbot commented Mar 5, 2017

The final comment period is now complete.

@aturon
Copy link
Member

aturon commented Mar 6, 2017

There's been no additional discussion since FCP opened, so I'm going to close this RFC as postponed. Thanks @phaazon!

@aturon aturon closed this Mar 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants