Skip to content

if case should be negatable #3865

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

Closed
feinstein opened this issue Jun 1, 2024 · 7 comments
Closed

if case should be negatable #3865

feinstein opened this issue Jun 1, 2024 · 7 comments
Labels
feature Proposed language feature that solves one or more problems patterns Issues related to pattern matching.

Comments

@feinstein
Copy link

feinstein commented Jun 1, 2024

Sorry if this issue already exists, but I couldn't find anything about this is the search.

I think it would be very useful if we could negate a if-case statement. In my code I wanted to execute something only if a pattern match happened, but I couldn't, so the only other option was using an else with an empty if-case body, which looked very ugly.

What I wanted:

// test case for when a particular go_router route is not the current route:
final matches = router.routerDelegate.currentConfiguration.matches;
if (matches case! [RouteMatchBase(matchedLocation: '/home'), ImperativeRouteMatch(matchedLocation: '/details')]) {
  fail('No route found');
}

What I can do now:

final matches = router.routerDelegate.currentConfiguration.matches;
if (matches case [RouteMatchBase(matchedLocation: '/home'), ImperativeRouteMatch(matchedLocation: '/details')]) {
  // Empty body because I can't negate the if-case
} else {
  fail('No route found');
}
@feinstein feinstein added the feature Proposed language feature that solves one or more problems label Jun 1, 2024
@lrhn
Copy link
Member

lrhn commented Jun 1, 2024

The main reason there is no negation of a pattern match, is that most pattern matches include capture variables, which are only available on the positive branch.
You have a pattern with no captures, which is why it makes sense to only care about the false branch.

I'm not sure that comes up often enough that it warrants specialized syntax.
You can also use a switch:

  switch (matches) {
    case [RouteMatchBase(matchedLocation: '/home'), ImperativeRouteMatch(matchedLocation: '/details')]: break
    default: fail('No route found');
  }

I'd consider it more likely (not highly likely, but more) that we'd allow (expression case pattern) and (expression case pattern when expression) as an expression, fully parenthesized, and not just as an entire if condition, #3062. That would allow you to write:

  if (!(matches case [RouteMatchBase(matchedLocation: '/home'), ImperativeRouteMatch(matchedLocation: '/details')])) {
    default: fail('No route found');
  } 

@lrhn lrhn added the patterns Issues related to pattern matching. label Jun 1, 2024
@feinstein
Copy link
Author

Wouldn't it run into the same issue?

I think we would expect a runtime exception or a compile time warning, if we tried to access a variable inside a pattern that was not matched.... Or a limitation that case! can't define variables for pattern matching.

@ghost
Copy link

ghost commented Jun 1, 2024

See #1548

@mmcdon20
Copy link

mmcdon20 commented Jun 1, 2024

C# has similar pattern matching features to dart, except they also have a not pattern:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns#logical-patterns

if (input is not null)
{
    // ...
}

And pattern matches are also expressions:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/is

static bool IsFirstFridayOfOctober(DateTime date) =>
    date is { Month: 10, Day: <=7, DayOfWeek: DayOfWeek.Friday };

@Wdestroier
Copy link

You can find this syntax suggestion in issue #2537.

The scoping rules of case! would be similar to Java patterns:
image

Java doesn't have an instanceof! / is not / is! operator, I belive most people would prefer if (shape instanceof! Rectangle) over if (!(shape instanceof Rectangle)), the same idea applies to if (shape case! Rectangle(final x, final y)) over if (!(shape case Rectangle(final x, final y))). Thus, if (a case! b) and if (!(a case b)) need to coexist.

@ghost
Copy link

ghost commented Jun 1, 2024

@Wdestroier : java doesn't allow shadowing. If you replace "Rectangle r" by "Rectangle s", the compiler will complain. The entire logic of negative conditions in java is based on this.
In dart, shadowing is quite common in "case" conditions.

What happens in java is this:

    public static void main(String args[]) {
        var name="abc";
        if (!(name instanceof String s)) {
            System.out.println("name is not an instance of String");
            return;
        } 
        System.out.println(s); 
    }

This works.
However, if you remove "return", the compiler will flag the last line: error: cannot find symbol s
The same will happen if you add an "else" block.
This is quite logical, and if dart can disallow shadowing in negative conditions, it may work in dart, too

(It should be case! - to rhyme with is!, and to avoid an awkward pair of parentheses)

@munificent
Copy link
Member

Thanks @tatumizer. Yes, I think this request is essentially a duplicate of #1548, or at least close enough that merging the issues is more helpful than not.

@munificent munificent closed this as not planned Won't fix, can't repro, duplicate, stale Jul 11, 2024
@Albert221 Albert221 marked this as a duplicate of #4273 Feb 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems patterns Issues related to pattern matching.
Projects
None yet
Development

No branches or pull requests

5 participants