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

[Proposal] Better handling of multiple comparisons with the same variable (a > min && a < max) #136

Closed
ashmind opened this issue Jan 29, 2015 · 13 comments

Comments

@ashmind
Copy link
Contributor

ashmind commented Jan 29, 2015

Problem

Consider these very common code patterns:

if (a > min && a < max) ...
if (value == Enum.Value1 || value === Enum.Value2) ...

I think it is worth considering how verbosity here can be reduced -- especially since it could also give a performance benefit for cases like

if (Slow() > min && Slow() < max) ...

Potential solutions

A. Ternary comparisons

For <, >, >=, and <= one solution can be Python-like min < a < max.

Pros:
  1. Good readability
  2. Performance benefit since a can be evaluated exactly once
Cons:
  1. Does not handle a == x || a == y.
B. Range comparisons

Example:

if (a in (min...max)) ...
if (value in (Enum.Value1, Enum.Value2)) ...
if (x in (1,2,3..7)) ...
Pros:
  1. Alright readability
  2. Covers many scenarios
  3. Can be easily adapted for future switch (case 1..3:).
Cons:
  1. Not clear whether the range is inclusive or exclusive
  2. Too much new syntax for a simple case?
C. Quantum superpositions

Example:

if (value == any(Enum.Value1, Enum.Value2)) ...

This is similar to Perl's Quantum Superpositions and something that was already implemented in Microsoft Research's .

Pros:
  1. Good readability
  2. Can be adapted for future switch
Cons:
  1. In practice only covers equality (x > any(1,2) is possible, but useless in most scenarios). Could be combined with solution A for a limited comparison support.
@HaloFour
Copy link

It was mentioned on CodePlex that functionality like this could be pretty easily accomplished through the pattern matching proposals and extension methods.

e.g.:

using static Some.Namespace.RangePatternMatcher;

if (x is between(5, 10)) { } // range comparison
if (x is any(1, 2, 3, 4, 10)) { } // set comparison

@AdamSpeight2008
Copy link
Contributor

NuGet: Exts.Ranging.Classes.Operators
Provides low < value < high via a lifted type.
low._() < value < high

if the type of the values are all ints, some coders think it should compile since you can't do bool < int
"Comparision operators" are overloadable and don't have to return a Boolean.

@ashmind
Copy link
Contributor Author

ashmind commented Jan 29, 2015

@HaloFour

It was mentioned on CodePlex that functionality like this could be pretty easily accomplished through the pattern matching proposals and extension methods.

Yes, both ranges and any can be used in pattern matching (this is what I meant by 'furture switch'), however it is more like "this feature can be used in pattern matching" than "pattern matching is essential to implement this feature".

I like the use of is though, consistent with type matching.

@rstarkov
Copy link
Contributor

I've pondered a similar feature before. Here's my proposal, which I'm going to repost partially from my blog.

The idea is to introduce "operators" and and or which would be used like so:

    if (SomeFunction(a, b, c) == 5 or 6 or 19) { ... }

which would translate into this:

    var temp = SomeFunction(a, b, c);
    if (temp == 5 || temp == 6 || temp == 19) { ... }

Or, to improve flexibility and readability we could require the operator to be specified separately for each value, like so:

    if (SomeFunction(a, b, c) != 5 and != 6 and != 19) { ... }

This also enables concise range comparisons, for example:

    if (SomeFunction(a, b, c) >= 5 and < 19) { ... }

and is nicely generalized, unlike, for example, the SQL between operator.

Full post where I discuss this in more detail. One downside is that this could be confusing for those who are used to "and" and "or" being the actual && and || operators, like Python.

@alrz
Copy link
Contributor

alrz commented Oct 17, 2015

What about if(-10 < a < 5) {}

@AdamSpeight2008
Copy link
Contributor

@alrz Workaround already exist you just need lift it into another type. Here

@ashmind
Copy link
Contributor Author

ashmind commented Oct 18, 2015

@alrz Is your proposal equivalent to "Solution A" above?

@alrz
Copy link
Contributor

alrz commented Oct 18, 2015

This can be done with a parameterized active pattern (#5718) or overloaded is operator.

class Between {
    public static bool operator is(int num, int from, int to) => num >= from && num <= to;
}

if(val is Between (-10, 7)) { }

And for equality test you can use disjunctive-pattern (#6235)

if(val is 4 or 10 or 60) {}

@AdamSpeight2008
Copy link
Contributor

@alrz using or wouldn't work well for vb.net. The extension methods work for both languages, see
Exts.*

@gafter
Copy link
Member

gafter commented Aug 15, 2016

The syntax a < b < c is already syntactically valid, so we cannot co-opt that to mean something else.

The problem with a syntax such as e in (0 ... n) is that we would have to pick which comparison operators to use, and whichever we pick will be confusing to many. I think the default choice would be closed on the lower bound and open on the upper bound, meaning 0 <= e && e < n.

You can at least avoid the reevaluation in C# 7 using patterns, for example, a < b && b <= c can be written b is var tmp && a < tmp && tmp <= c.

It isn't clear there is a palatable direct solution to this problem. It doesn't arise often in code, so whatever solution we came up with would really only be a marginal improvement to the language. We don't see a solution we feel we can take.

@gafter gafter closed this as completed Aug 15, 2016
@alrz
Copy link
Contributor

alrz commented Aug 15, 2016

@gafter A range expression e.g. 0 .. 6 along with a range pattern e.g. value is 0 .. 6 is what other languages offer. I would totally prefer foreach(var index in 0 .. arr.Length) to a for statement to take advantage of foreach revised variable capture and a slightly cleaner code.

@gafter
Copy link
Member

gafter commented Aug 15, 2016

@alrz The foreach proposal is interesting, but different from this request.

@alrz
Copy link
Contributor

alrz commented Aug 15, 2016

@gafter It was mentioned in #7632. The pattern counterpart is something that one would expect.

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

No branches or pull requests

9 participants