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

Champion: Switch expression as a statement expression #2632

Open
gafter opened this issue Jul 4, 2019 · 67 comments
Open

Champion: Switch expression as a statement expression #2632

gafter opened this issue Jul 4, 2019 · 67 comments
Labels
Milestone

Comments

@gafter
Copy link
Member

gafter commented Jul 4, 2019

I propose that we support a switch expression as a statement expression when every arm's expression is also a statement expression. No common type among the arms is required when used as a statement expression.

void M(bool c, ref int x, ref string s)
{
    c switch { true => x = 1, false => s = null };
}

Meeting notes

https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-08-31.md#switch-expression-as-a-statement

@gafter gafter self-assigned this Jul 4, 2019
@DavidArno
Copy link

Just to check, since I get confused between statement expressions and expression statements, will the following syntax by valid with this proposal:

void M(bool c, ref int x, ref string s) => c switch { true => x = 1, false => s = null };

@Mteheran
Copy link

Mteheran commented Jul 5, 2019

Is the operator for default case still the same for this scenario or this new feature??

As I understand underscore is the current operator

void M(bool c, ref int x, ref string s) { c switch { true => x = 1, false => s = null, _ => x=0 }; }

@dsaf
Copy link

dsaf commented Jul 6, 2019

Together with exhaustiveness checks this will render existing switch statement pretty much legacy?

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Jul 6, 2019

@dsaf how so? You still can't have things like blocks/multi-statements in an expression-form switch.

@dsaf
Copy link

dsaf commented Jul 6, 2019

@CyrusNajmabadi well maybe initially but those could be added going forward.

@alrz
Copy link
Contributor

alrz commented Jul 7, 2019

Together with exhaustiveness checks this will render existing switch statement pretty much legacy?

Non-exhaustive switches can't be written via switch expression,

void M(string s) {
    switch (o) {
        case 1: s = "A"; break; 
        case 2: s = "B"; break;
    }
}

Unless this is allowed

void M(string s) {
    o switch { 1 => s = "A", 2 => s = "B" };
}

(You still need to repeat the assignment.)

@CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi well maybe initially but those could be added going forward.

In that case, they do not "render existing switch statement pretty much legacy". :)

@Thaina
Copy link

Thaina commented Jul 7, 2019

What happen to

var a = c switch { true => x = 1, false => s = null };

?

@DavidArno
Copy link

@Thaina,

It seems sensible that it'll give you a error CS0815: Cannot assign void to an implicitly-typed variable

@Thaina
Copy link

Thaina commented Jul 8, 2019

@DavidArno We actually could write this

int x = 0;
var a = x = 1; // a is int

And in the next C# I think we could possible to

var a = condition ? 1 : null; // a become Nullable<int>

So actually I expect that it might be possible to

var a = c switch { true => x = 1, false => s = null };
// a become Nullable<int> or object or (int | string)
// then assign both x and a to 1 if true else assign both s and a to null

So I just ask to make sure

@yaakov-h
Copy link
Member

yaakov-h commented Jul 8, 2019

If s is a string, then you get error CS8506: No best type was found for the switch expression.

If s is a Nullable<int>/int?, that already compiles, and I see no reason why that would change.

@Thaina
Copy link

Thaina commented Jul 8, 2019

@yaakov-h It possible that it's behaviour could be CS0815 as @DavidArno speculate. Or maybe other. That's why I ask to make sure what it really would be

As for me I hope that we should support union type and could let this syntax return union type. Or just plain object

@DavidArno
Copy link

@Thaina,

The value of, c switch { true => x = 1, false => s = null };, isn't ?int though; it's void. Just as I can write,

void Foo() {}
void Bar() => Foo();
void Baz()
{
    var x = Foo(); // results in error CS0815: Cannot assign void to an implicitly-typed variable
}

So the same should be true of a statement expression switch expression,

void Bar(bool c, ref int x, ref string s) 
    => c switch { true => x = 1, false => s = null }; // all good
void Baz(bool c, ref int x, ref string s)
{
    var a = switch { true => x = 1, false => s = null }; 
    // results in error CS0815: Cannot assign void to an implicitly-typed variable
}

@Thaina
Copy link

Thaina commented Jul 8, 2019

@DavidArno

c switch { true => 1, false => null };

This expression should be int? though. So

var a = c switch { true => 1, false => null }; // a is int?

Given that

var a = x = 1; // a is int

Expression x = 1 itself is int, not void. So even if we put it in switch it should still be int

C# also already has the ability to ignore any return type to void with => syntax

int Bar() => 0;
void Foo() => Bar();

So I don't think we need to limit switch expression to be void

@DavidArno
Copy link

@Thaina,

Sure, var a = c switch { true => 1, false => null }; should reasonably be expected to result in a being a ?int under proposals to support var a = c ? 1 : null;. But that's completely different to:

c switch { true => x = 1, false => s = null };

where x = 1 and s = null are statement expressions (ie have the "value" of void) and thus this proposal to allow the whole switch expression to be treated as a stament expression (ie have the "value" of void).

@Thaina
Copy link

Thaina commented Jul 8, 2019

@DavidArno My point is, even in today C#. The statement expressions is not void but it would be the type of the variable of that expression

This below is valid

bool b = someCondition;
if(b = true) // valid and work, it assign true to b and then check with value of b
{
	
}

@DavidArno
Copy link

DavidArno commented Jul 8, 2019

@Thaina,
That's a fair point. Perhaps therefore var a = switch { true => x = 1, false => s = null }; could result in a being a ?int. I was going to suggest that @gafter's example was a confusing one and that something like:

void M(bool c, ref int x)
{
    c switch { true => x = 1, false => Console.WriteLine("hello" };
}

is clearer over it being a statement expression. But this example would have masked your question, so it was a good example to choose.

Edited as @yaakov-h has pointed out that, since there's no common type between int and string, the only valid result of the expression has to be void.

@yaakov-h
Copy link
Member

yaakov-h commented Jul 8, 2019

Just to clear things up a bit, the sample code @Thaina provided is already valid if you declare those variables correctly:

Sharplab Playground.

@Thaina
Copy link

Thaina commented Jul 9, 2019

@DavidArno There is common type between int and string which is object

And in the future we might have union type like a typescript. Which should be used in this scenario

As I said I just want to have clear official statement on this

@gafter gafter added this to the 8.X candidate milestone Jul 17, 2019
@gafter gafter modified the milestones: 8.X candidate, 9.0 candidate Aug 28, 2019
@gafter
Copy link
Member Author

gafter commented Aug 28, 2019

If this is not too complex we'd like to include it in C# 9.0.

@ronnygunawan
Copy link

ronnygunawan commented Oct 4, 2019

Is it still not a good idea to have switch expression with method body?

var x = o switch {
    Rectangle r => r.Width * r.Height,
    Circle c => {
        var radius = c.Diameter / 2; // allow multiple statements in one case
        Debug.WriteLine(radius); // allow side effects and break points
        return Math.PI * radius * radius;
    },
    _ => throw new NotImplementedException()
};

Switch expression as statement expression:

o switch {
    Foo f => {
        f.Bar();
        ...
    },
    _ => {
        ...
    }
}

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Sep 8, 2022

@ccarlo88 The sarcasm and abusive comments are not welcome. Please read the code of conduct here: https://dotnetfoundation.org/about/code-of-conduct

Especially these parts:

  • Demonstrating empathy and kindness toward other people
  • Being respectful of differing opinions, viewpoints, and experiences
  • Giving and gracefully accepting constructive feedback
  • Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience

There are tons of people who visit here who have differing opinions on things. Nearly all of them are able to constructively share those ideas and discuss them with others. If you cannot, please find some other place to express your negativity.

@ccarlo88
Copy link

ccarlo88 commented Sep 8, 2022

@CyrusNajmabadi sorry for anyone who may be affected. I am just a guy (from many) who got tired since dotnet became a "foundation".

@CyrusNajmabadi
Copy link
Member

@ccarlo88 That's fine. Just keep the tone civil from now on, and there won't be a problem. Thanks! :)

@Crauzer
Copy link

Crauzer commented Feb 18, 2023

Any update on this ?
It would be great to have this instead of the ugly switch with breaks or regular branching.

@ccarlo88
Copy link

It would be great if people stop creating new syntax in C#. Some reasons to not implement that:

  1. If it is a 2 element you can use a ternary operator:
    https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator

  2. If there's more I don't expect you to put everything in one line. So, you have the standard switch or the new switch expression:
    https://learn.microsoft.com/en-US/dotnet/csharp/language-reference/operators/switch-expression

  3. Changing 2 variables in one line was hard for me to even notice that you were changing on true 's' but on false 'x'

  4. C# is supposed to be the evolution of C++ but is becoming a weird mixture of other languages. If people keep changing it, it gets hard for people to read each other's code.

@HaloFour
Copy link
Contributor

@ccarlo88

C# is supposed to be the evolution of C++ but is becoming a weird mixture of other languages. If people keep changing it, it gets hard for people to read each other's code.

Yet C++ continues to evolve and gain new features and syntax, often borrowed from other languages. For example, C++20 has co_await, to be used in asynchronous coroutines, which was clearly borrowed from C#.

As long as there are friction points in the language worth addressing the language will continue to evolve. The switch statement, as it was inherited from C++, can certainly be considered a wart that has a weird syntax and behavior compared to contemporary languages.

@ccarlo88
Copy link

@HaloFour You pointed 2 different points, syntax and features.
Please, correct me if I am wrong but co_await is regarding feature and the issue here is regarding syntax.
Feature is always welcome but creating different syntax is an issue. Not even Google, who is a complete disappointment regarding programming syntax, keeps including "alternative" syntax.

@HaloFour
Copy link
Contributor

@ccarlo88

Feature is always welcome but creating different syntax is an issue.

I don't see much of a distinction between the two. If the existing syntax creates friction in common use cases then there is nothing wrong with introducing alternative syntax to improve the developer ergonomics and to create a pit of success. It's not all that common that a new syntax maps to completely new features that didn't already have possible solutions.

Not even Google, who is a complete disappointment regarding programming syntax, keeps including "alternative" syntax.

I've yet to meet a programming language that hasn't adopted additional syntax to smooth over friction in existing features. The switch statement, as inherited from C++, is very awkward to use. Ternary operators are fine for chained boolean conditionals, but switch, given it's integration with pattern matching, is capable of a lot more.

I'm sure that there is plenty of syntax that has been added to C# over the past 22 years that you would find challenging to live without today, even if there are ways to accomplish the same exact features in C# 1.0. I'm sure that there are places in the language where you think things could be made more concise or more declarative in order to simplify tasks you commonly perform. Everyone has a different subset of features they use or want to see added.

@ccarlo88

This comment was marked as off-topic.

@CyrusNajmabadi
Copy link
Member

This issue tracks the design/spec work for this feature. Please let the conversation on topic. If you want to talk about meta topics, please open a separate discussion. Thanks.

@HaloFour
Copy link
Contributor

@ccarlo88

friction in a switch? Again, C++ -> C#, evolution.

Yes, even in C# 1.0 the switch statement felt archaic, largely because it had already felt quite archaic in C++. The use of labels, weird scoping rules, automatic fall-through. At least C# intentionally prevented the last case, given how ripe it was as a source of bugs. It's also really awkward to use with expressions, which, like it or not, are much more common nowadays.

In fact, giving my opinion, people who doesn't like how C# syntax is, could find an alternative programming language.

The same is true in reverse; if you don't like the evolution of C#, you can find an alternative programming language. However, I would posit that any programming language that isn't continuing to evolve and add new syntax is a dead language. Any language that has any active usage continues to evolve, including C++, COBOL, Fortran, BASIC, Lisp, SQL, Ada, Java, etc. Or you can continue to use older versions of C#.

The C# language evolves fairly conservatively, features need to prove themselves worth the massive effort to be implemented, and purely syntactic changes have the highest bar. But the language is not going to simply stop evolving.

@ccarlo88

This comment was marked as off-topic.

@CyrusNajmabadi
Copy link
Member

I'm not certain what was unclear about my last post. If you want to discuss meta points like these, start a new discussion

@oliverjanik
Copy link

This would be super handy in unit tests with most Assert methods being void when testing polymorphic returns.

result switch
{
    int i => Assert.Equal(i, 5),
    string s => Assert.Equal(s, "x"),
    _ => Assert.Null(result),
}

@jsmarsch
Copy link

@Pilchard123 returning discard seems fitting for noop branch like (Behaviour.NoOp)=>_, which would read as discard this branch

I like that idea. Viewing the switch statement as a state machine, it makes sense that I would sometimes want to execute code that does not have a return statement.

@ccarlo88
Copy link

ccarlo88 commented May 6, 2023

This would be super handy in unit tests with most Assert methods being void when testing polymorphic returns.

result switch
{
    int i => Assert.Equal(i, 5),
    string s => Assert.Equal(s, "x"),
    _ => Assert.Null(result),
}

Yeah, the issue that you mention looks different to me. First, why don't you use "typeof" to determine what type you are evaluating? Then you use a switch case.
I still look at the request here and it makes no sense (the original one).

@alexrp
Copy link

alexrp commented May 6, 2023

First, why don't you use "typeof" to determine what type you are evaluating?

Can you demonstrate how typeof helps in that example?

@ccarlo88
Copy link

ccarlo88 commented May 7, 2023

Maybe I misunderstood that creepy thing... What it does look to me is receiving a generic T, and depending on T type it process something on that type.... am I wrong? Anyway, this is really creepy, looks like the typical Python crap.

@CyrusNajmabadi
Copy link
Member

@ccarlo88 Switch expressions receive a value and allow patterns to match against it. That's been in the language a while now. This issue is the request for allowing the args of the switch expression to be void expressions, and for the switch expression to be considered a legal statement-expression (like Assert.Equal is).

@ccarlo88

This comment was marked as off-topic.

@ccarlo88

This comment was marked as off-topic.

@CyrusNajmabadi
Copy link
Member

@ccarlo88 please keep things on-topic and following the .net code of conduct. WRT the language, the group that designs the language (the LDM) is the same one that has been designing it for 25 years now, with most of the same people involved over all that time.

@ccarlo88

This comment was marked as off-topic.

@CyrusNajmabadi
Copy link
Member

@ccarlo88 please keep things on-topic and following the .net code of conduct. If you have feelings beyond that, please take them to some other outlet (a blog or discord channel would be better).

@ccarlo88

This comment was marked as off-topic.

@dotnet dotnet locked as spam and limited conversation to collaborators May 7, 2023
@CyrusNajmabadi
Copy link
Member

Locking temporarily as moderation is not being listened to.

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

No branches or pull requests