-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
What are the semantics of 'goto case' in the presence of pattern-matching? #8821
Comments
Don't allow |
@DavidArno The ordinary |
Is that really true? Surely the current With expressions though, everything changes. Now, the order is very important, as each expression must be tested in that precise order and both fall-through and My interpretation of your thoughts in #4944 was that the current |
Yes, it is really true. Our intent is that the extension of
Patterns are not general expressions, and the specification does not say what order the matching occurs - in fact, it is explicitly unspecified. It only says that the first case that matches is the one whose body is executed.
I haven't yet seen any examples where confusing things would happen. Can you think of any? |
The following code example tries to capture some of the things that could be confusing if switch (shape)
{
case Line:
case Square(var length) when length < 5.0: // fall-through would be allowed here?
Action1();
break;
case Square(var length): // should the compiler emit a warning that length isn't used?
default: // should the compiler emit a warning that default isn't the
// last case?
Action2();
break;
case Circle(var radius): // should the compiler give an error here that we are
// mixing vars?
case Ellipse(var r1, var r2): // or here?
Action3(radius); // or here, as radius may not exist in this context?
break;
case Rectangle:
Action4();
goto case Square; // and the topic of this thread: how do I use goto with
// patterns? Really bad case would be
// goto case Square(var length) when length < 5.0,
// does that goto the second case or to
// case Square(var length)
// only when length < 5, ie does it perform the check before the
// goto? What happens if the check fails?
} Next, whilst it will now be possible to switch on non-constants, the syntax will be really weird. I couldn't do: var a = new SomeType();
var b = new SomeType();
var c = new SomeType();
var d = a;
switch (d)
{
case a:
Action1();
break;
case b:
Action2();
break;
case c:
Action3();
break;
default:
Action4();
break;
} Instead, it would need to be: var a = new SomeType();
var b = new SomeType();
var c = new SomeType();
var d = a;
switch (d)
{
case var x where x == a:
Action1();
break;
case var x where x == b:
Action2();
break;
case var x where x == c:
Action3();
break;
default:
Action4();
break;
} In the above case, it's clear why the syntax is like that, but in more complicated situations, it could be very unintuitive for many as to why they need to switch from one syntax to another when writing a None of the above issues are insurmountable. However, I feel they highlight some of the ways in which just extending
You seem very much in favour of the former. To my mind at least, this is a mistake as it opens up a whole serious of potential areas of confusion. If we go down the route of providing a statement-based form of pattern matching, it is clearly delineated into existing functionality with one syntax and a whole new type of functionality with another. Writing: match using (d)
{
case var x where x == a: Action1();
case var x where x == b: Action2();
case var x where x == c: Action3();
default: Action4();
} creates something syntactically distinct. It's obvious to the developer that they are using something very different to (I've used |
@DavidArno You don't goto patterns, you goto with an expression and the In the first case, In the second case Per @gafter's comment:
But I think it doesn't need to, because in case of any misuse, variables become definitely not assigned and you will know that something's wrong (but it must be handled last — it's not a synonym for In any language that feature pattern-matching there is no dedicated switch statement for pattern-matching and regular switch, because basically the former is a superset of the latter. But they do have two forms for expression and statement contexts (if they are not expression-based). |
I'm assuming that - given the existence of this issue - your assertion is far from certain.
Do you know of any other language that has added pattern matching after defining My main motivator for wanting pattern statements to not use |
It's not an "assumption". See #7703,
You gotta check out
It's not! because constants are basically a constant-pattern so you'd have the same behavior with an extended switch construct. It's not something else.
The only difference that I see in your proposed syntax is the removed For other points you've made I woudn't repeat my answer. |
While probable this isn't the first time it's come up: #7703 And that does make sense as you can interpret I like the idea of having pattern matching permeate the language, and adding support to existing language constructs like |
@HaloFour What is wrong with |
@alrz My biggest beef would probably be the lack of scoping between the cases. C# did at least correctly identify and prevent accidental fall-through which was it's biggest pit of failure. But because of that the requirement of I propose nothing to address these concerns since that ship has already sailed. |
Given the scenario where every |
@HaloFour While I agree that lack of scoping between the cases is not helping you're free to use blocks to define separate scopes. But I believe all this is because C# begins with a C. And if you're gonna argue with that you'd better off with another language. @DavidArno You can also put the All in all, inventing yet another syntax on top of this with the exact same semantics to solve the exact same problem seems like this. |
@alrz Sure, you can use blocks. But |
@HaloFour The Legacy made it to 2017. If that was absurdly a mistake they should have abandoned it a long time ago. My thinking is that |
I get your point re the XKCD comic and it's one well made. In reality though, we have The scenario as I see it would be |
@DavidArno As I said, |
I don't follow your " A real-world example, would be: public sealed class Union<T1, T2, T3, T4>
{
private readonly Dictionary<Variant, Func<int>> _hashCodes;
...
private Union()
{
_hashCodes = new Dictionary<Variant, Func<int>>
{
{Variant.Case1, () => _value1.GetHashCode()},
{Variant.Case2, () => _value2.GetHashCode()}
{Variant.Case3, () => _value3.GetHashCode()}
{Variant.Case4, () => _value4.GetHashCode()}
};
...
}
...
public override int GetHashCode() => _hashCodes[Case]();
} Obviously with C# 7, I'll be able to rewrite that as: public override int GetHashCode() => Case match(
case Variant.Case1: _value1.GetHashCode()
case Variant.Case2: _value2.GetHashCode()
case Variant.Case3: _value3.GetHashCode()
case Variant.Case4: _value4.GetHashCode()
); which will be nice. The point is though, dictionaries can already be used to replace |
Some people don't like the slightly more bloated syntax so they opt for the significantly more bloated execution? This is why I generally avoid stackoverflow. |
It has one delegate, but multiple instances per each case, don't get me started on captured variables etc. Please don't. |
You don't want to use a switch statement. You want to use a match expression. |
Will that work even if the statement for each case is a void, I've like F#'s unit expressions? |
That is weird, because accouring to Eric Lippert,
So this is allowed: void F() => G(); And since this would be allowed: void F(T e) => e match(case P : G()); It'd be unfortunate that this woudn't be allowed: {
e match(case P : G());
} I might really prefer curly braces and allow this: {
e match {
case P : G()
}
} perhaps, without a semicolon at the end. |
I do not expect to allow a match expression to be of type
Unlikely if |
So, to my understanding it's an expression in the same sense as a ternary, void G() { .. }
void F() => cond ? G() : G(); Because this also woudn't work if either |
The two statements,
and
are at odds with each other. Under the proposed spec, I can't do the following: switch (d)
{
case var x where x == a: Action1();
case var x where x == b: Action2();
case var x where x == c: Action3();
default: Action4();
} If I'm dealing with var x = match (d)
{
case var x where x == a: Func1();
case var x where x == b: Func2();
case var x where x == c: Func3();
default: Func4();
} Whereas, if I'm dealing with switch (d)
{
case var x where x == a:
Action1();
break;
case var x where x == b:
Action2();
break;
case var x where x == c:
Action3();
break;
default:
Action4();
break;
} Or the hard to read: switch (d)
{
case var x where x == a: Action1(); break;
case var x where x == b: Action2(); break;
case var x where x == c: Action3(); break;
default: Action4(); break;
} |
We have already implemented the intended semantics in the |
We need a language specification for the intended behavior of
goto case
when the switch contains pattern-matching constructs. And then we need to implement that.The text was updated successfully, but these errors were encountered: