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

Macroless mapping of patterns -> bool #2153

Closed
alexheretic opened this issue Sep 15, 2017 · 9 comments
Closed

Macroless mapping of patterns -> bool #2153

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

Comments

@alexheretic
Copy link
Member

alexheretic commented Sep 15, 2017

The current if let $pattern = $expr {} functionality is great when you're de-structuring parts of the pattern for use in the if-scope. However, when matching without that need it can feel quite limited. I would suggest allowing macroless evaluation of certain pattern matches -> bool.

Current State

Example compile error:

enum Example {
    A(i32, &'static str),
    B(i32, bool),
}

let example = Example::A(123, "alex");
let always_do_it = true;

if let Example::A(.., "alex") = example || always_do_it {
    // compile error!
}

This makes sense when you can reference part of the pattern within the if-scope, but less so when it's a straight forward match.

The macroless way of mapping a pattern match to a boolean can currently be a little ugly.

if if let Example::A(.., "alex") = example { true } else { false} || always_do_it {
    // works, but ugly enough to have to split out
}

let examples: IntoIter<Example> = ...

// filter away the A-123 cases, difficult to read
examples.filter(|t| if let Example::A(123, ..) = *t { false } else { true });

New functionality

How about we allow pattern matches without a variable binding to be evaluated as bools.

let is_a_123: bool = let Example::A(123, ..) = example;

This would mean the above examples would start working / could be more readable.

if let Example::A(.., "alex") = example || always_do_it {
    // works now
}

examples.filter(|t| !let Example::A(123, ..) = *t) // nicer

Its worth noting explicitly that I would expect patterns with variable bindings to not be allowed

// compile error! variable bindings are not allowed in pattern -> bool evaluations
let is_a_123: bool = let Example::A(123, some_str) = example;

This means this functionality would have a clear distinction from the current if let $pattern = $expr {} usage.

Working example

I'd like this as macroless functionality built into the language. However, I can provide a simple macro for a working example.

macro_rules! iff { 
    (let $p:pat = $e:expr) => {{
        if let $p = $e { true } else { false }
    }};
}

let is_a_123: bool = iff!(let Example::A(123, ..) = example);

if iff!(let Example::A(.., "alex") = example) || always_do_it {}

examples.filter(|t| !iff!(let Example::A(123, ..) = *t));

These are not nice, but show the functionality working as close as we can currently get.

Comments

I think this would make a good addition to the current if let behaviour. So please fill me in on all the obvious stuff I've missed!

@oli-obk
Copy link
Contributor

oli-obk commented Sep 18, 2017

if let Example::A(.., "alex") = example || always_do_it {

This looks to me like if let Example::A(.., "alex") = (example || always_do_it) {

Bikeshed:

Contextual keyword matches:

let is_a_123: bool = example matches Example::A(123, ..);
if example matches Example::A(.., "alex") || always_do_it {}
examples.filter(|t| !(*t matches Example::A(123, ..)));

Or is matching + is not matching as contextual sentences 😆

@alexheretic
Copy link
Member Author

This looks to me like if let Example::A(.., "alex") = (example || always_do_it) {

Hmmm, yes I suppose since the assignment operator has the least precedence it would seem like that. That's a big problem with the proposal, thanks for pointing that out.

Contextual keyword matches

I totally agree. But I didn't suggest this as I thought adding a new keyword to the language would be too difficult. The strength of enhancing let $pattern = $expr usage would be backwards compatibility.

@alexheretic
Copy link
Member Author

I suppose the question now is: Can we do this in a backwards compatible way / without adding a new keyword?

Could the match keyword take on an additional use, similar to what you suggested?
It'll essentially become a binary operator with high precedence $expr match $pattern.

let is_a_123: bool = example match Example::A(123, ..);
if example match Example::A(.., "alex") || always_do_it {}
examples.filter(|t| !(*t match Example::A(123, ..)));

@oli-obk
Copy link
Contributor

oli-obk commented Sep 18, 2017

The strength of enhancing let $pattern = $expr usage would be backwards compatibility.

The problem with reusing the same syntactical pattern is that it's very weird for the refutability of a pattern to change the type of the let binding.

@petrochenkov
Copy link
Contributor

See #929 (comment)

@alexheretic
Copy link
Member Author

Ultimately I believe we need a better solution to the over-verbose:

if let $pattern = $expr { true } else { false }

I'm not sure this issue is helping though, as it isn't a good enough proposal and isn't going anywhere.

@SimonSapin
Copy link
Contributor

I while ago I submitted #163 for adding a matches!($expression, $pattern) macro to the standard library. It was rejected because at the time there was no mechanism to add it without having it in the prelude. Maybe with Macros 2.0 it’s more doable now?

@alexheretic
Copy link
Member Author

Indeed, and a macro/if-else is what I use now for this.

This was about getting the functionality into the language without macros or verbosity. This is something I still feel is worthwhile.

@Centril
Copy link
Contributor

Centril commented Feb 23, 2018

Linking #2260

@Centril Centril added the T-lang Relevant to the language team, which will review and decide on the RFC. label Feb 23, 2018
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

5 participants