-
Notifications
You must be signed in to change notification settings - Fork 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
Proposal: Permit variable declarations under disjunctive patterns #4018
Comments
cc @333fred |
I will champ ion this. |
I imagine we can crib a lot of the rules/spec from F# since they offer this functionality? |
@HaloFour Definitely. F# specification on the subject did help a lot already.
Beyond that, we are diverging from F# specification because in F# it's an error if you don't have an identical set on each side, but in C# we could make additional variables unassigned in that case. This is useful in a switch statement with a |
Lol @CyrusNajmabadi I had already told alrz I'd champion this. It's mine! No fair waking up before me. |
WFM :) I was championing in case no one else picked it up. But if you want it, go for it :) |
I want us to address this scenario! I am a bit concerned about a feature that has multiple declarations of the same variable, so I would want to consider alternatives that didn't do that. For instance, maybe we have a pattern that can match into an existing variable if the type is right. E.g. (just thinking out loud here): e switch
{
(int i, 0) or (0, i) => i,
} Where specifying an existing variable matches into that variable. Perhaps we would only allow it if the variable is not already assigned, to avoid pattern matching having side effects. I'm not saying this is better, but it has different trade-offs, and we should consider our options. |
Some thoughts on that syntax:
|
Would that mean enabling using existing variables in pattern variables in general? int i;
e switch {
(i, 0) or (0, i) => i
} |
What if I want to match instead of assign? readonly int i = 0;
e switch {
(i, 0) or (0, i) => ...
} And that already works for constants with the exact same syntax. I've suggested |
Exactly, that syntax wades into the territory of requests to allow existing variables to be used in patterns instead of just constants, as well as being able to assign patterns into existing variables. |
And to be clear, the said variables are usually defined outside of the pattern itself and likely unassigned (e.g. out parameters) I'd even say that if it ever become a thing, it should be disallowed to reassign pattern variables, that'll make the pattern itself difficult to reason about and hard to follow where such variables are assigned and possibly reassigned. So I favor "multiply declared variables" syntax where we explicitly re-declare variables, rather than implicitly re-assigning them. |
From LDM:
It seems like to allow this we only need to relax the existing scoping rules around pattern variables to enable multi-declarations and handle definite-assignment accordingly. i.e. In other words, the rules that flag those name conflicts are already there, we only need to relax those existing scoping rules. Having said that, the support for overlapping variables across separate patterns just falls out of the fact that they share the same scope. Unless we want to disallow it explicitly.
|
We still need the specese.
Consider: if (o is (not null, null) a or (null, not null) a) {} Also remember the |
I want to add that I wish I could reassign variable. So that I could chain it in Instead of var a = GetFrom0();
if(a == null)
a = GetFrom1();
if(a == null)
a = GetFrom2();
if(a == null)
return;
// DoSomething with a I could just if(GetFrom0() is var a || GetFrom1() is {} a || GetFrom2() is {} a) // Should it require reassign to be {} only ?
{
// DoSomething with a
} |
Can't you use: if ((GetFrom0() ?? GetFrom1() ?? GetFrom2()) is var a)
{
// DoSomething with a
} |
That's the second part of the proposal where you could possibly redeclare pattern variables and they assign to the same local
|
@alrz Oh then sorry I miss that part |
@spydacarnage Not only that sometime I want to do something like this void DoSomething(SomeObject obj)
{
if(obj != null || GetFrom0() is {} obj || GetFrom1() is {} obj || GetFrom2() is {} obj) // reassign variable
{
// DoSomething with obj
}
} And there could be more complex logic in between void DoSomething(SomeObject obj)
{
if(obj != null || (GetFrom0() is {} obj && obj.Val0 != null) || (GetFrom1() is {} obj && obj.Val1 != null))
{
// DoSomething with obj
}
} |
Permit variable declarations under disjunctive patterns
Design meetings
https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-11-11.md#variable-declarations-under-disjunctive-patterns
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-03-28.md#variable-declarations-under-disjunctive-patterns
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-28.md#ungrouped
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-10-17.md#permit-variable-declarations-under-disjunctive-patterns
https://github.com/dotnet/csharplang/blob/main/meetings/2024/LDM-2024-09-06.md#permit-variable-declarations-under-disjunctive-patterns
The text was updated successfully, but these errors were encountered: