language
language copied to clipboard
`if case` should be negatable
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');
}
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');
}
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.
See #1548
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 };
You can find this syntax suggestion in issue #2537.
The scoping rules of case! would be similar to Java patterns:
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.
@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)
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.