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

[Proposal] make 'switch' more real-life usable #959

Closed
XuTpuK opened this issue Mar 1, 2015 · 41 comments
Closed

[Proposal] make 'switch' more real-life usable #959

XuTpuK opened this issue Mar 1, 2015 · 41 comments

Comments

@XuTpuK
Copy link

XuTpuK commented Mar 1, 2015

'switch' operator was blindly copied from C/Java, but 1) by some strange reason lost ability to fall from one branch to another 2) still unusable like in original languages

What is "nature" of 'switch'? SWITCH code branches, running exactly one of 'em! So if I declare branch "case 1: do_that", why the hell I should worry about all that "break"s, "fall through" and so on?!

Switch should BE the switch:

switch(a) {
case 1: action1(); action2(); goto case default;
case 2: { int z = a; }// local scope
default: z = 4;
}

That's it! Why we like monkeys still repeat invention of 70-th "case 1: .... break;" if we even cannot fall through?! Good question to designers of C#!

@gafter
Copy link
Member

gafter commented Mar 1, 2015

@XuTpuK Are you suggesting that we should make the "break" optional?

@sharwell
Copy link
Member

sharwell commented Mar 1, 2015

@XuTpuK The issue of fall-through came up in another discussion; my comment there gives an example of the current C# syntax for fall-through.

My interpretation of the overall request is the same as @gafter (a request to make break; optional). Can you confirm that we understood you correctly?

For background, you may also be interested in the following two very informative comments from Eric Lippert. They explain the details of the current syntax as well as the original reasoning for requiring a break statement.

  1. Why do I need to use break?
  2. How can I use more than one constant for a switch case in C#?

@XuTpuK
Copy link
Author

XuTpuK commented Mar 2, 2015

@sharwell I don't think "break" is needed at all - that's the point! EVERY "case" should be implicitly finished with jump to the end of "switch".
I read what Eric said, but he just juggle with useless terminology, giving no any reason why the hell we deal with "break" at all! Nobody cares is it "statement list" or "code branch", we care only about ONE thing: one of many branch should run. Period.

@gafter
Copy link
Member

gafter commented Mar 2, 2015

@XuTpuK Just to make sure I understand, you'd prefer that if a programmer writes

    switch (i)
    {
        case 1:
            // no statements
        case 2:
            Console.WriteLine("two");
    }

Then this should print two when i == 2, and not print anything when i == 1. Is that correct?

@ibrahimbensalah
Copy link

i need a downvote here ;P

@XuTpuK
Copy link
Author

XuTpuK commented Mar 2, 2015

@gafter Nope. Both cases (1 & 2) are labels for one statement block.

@Joe4evr
Copy link

Joe4evr commented Mar 2, 2015

Both cases (1 & 2) are labels for one statement block.

For some reason I thought this was already possible, but then I tried it real quick and apparently it's not. (Weirdly enough, it says Control cannot fall through from one case label ('case: 2') to another. or whichever case is on the bottom. I didn't know falling happened from the ground up. 😆)

@sharwell
Copy link
Member

sharwell commented Mar 2, 2015

@XuTpuK The rationale used during the original design which included the break statement is part of the first link I posted. Here is the specific excerpt:

If your question is better stated as "why doesn't the compiler automatically fix my error when I fail to produce a switch section with a statement list with an unreachable end point, by inserting a break statement on my behalf?", then the answer is that C# is not a "make a guess about what the developer probably meant and muddle on through" sort of language. Some languages are -- JScript, for example -- but C# is not.

This doesn't mean it can't change, but it's still a relevant point in favor of including an explicit break.

@Joe4evr The error with the code is due to missing a break at the end (before the }), not between the two case labels.

@Joe4evr
Copy link

Joe4evr commented Mar 2, 2015

@sharwell Oh, derp.

So that part about using multiple case labels for a single statement block is already possible just fine. Then yeah, there is no added value to this proposal other than attempting to make break implicit, which isn't happening as per the above quote. You want to fall through to another block? Use goto case. You want to get out of the switch? Use break. Don't like break? Then just go and write if {} else if {} else instead.

@chrisaut
Copy link

chrisaut commented Mar 2, 2015

@ibrahimbensalah You need just a bit more brains, not a "downvote". I cannot explain every beginner >why code should be short, obvious and predictable, just believe me - 'switch' in current form is a stupid >legacy.

It's probably not my place to say this, but can we enforce some sort of etiquette on here? I've noticed this is not XuTpuK's first case where he/she is using an extremely confrontational tone.

Telling other users that they need a "brain" and patronizing them as beginners surely does not foster a good discussion environment.

@GeirGrusom
Copy link

I think the rationale for making break obligatory is excellent. It's not a useful feature, and when you need fall-through it's very easy to make it work anyway.

If the switch needed any rework it would be to make it act more like expressions i.e. that they can return a value, and look more like classes instead of a goto-list.

@mkosieradzki
Copy link
Contributor

@chrisaut +1

I absolutely agree that break should be non-optional. In my opinion it's worth to make switching easier/shorter but through functional-style pattern-matching constructs, which don't have "follow-up" issues because they always end with an implicit "return".

But there is already an active discussion about pattern-matching style constructs in #206 .

@ibrahimbensalah
Copy link

@XuTpuK I can't argue with you about my brain, thinking about it reminds me of the Halting Problem.

@gafter
Copy link
Member

gafter commented Mar 2, 2015

@XuTpuK We of small brains need things explained using small words. Please be patient and help us become more than beginners.

@paulomorgado
Copy link

The fallthrough is lost is C# because compilers are free to reorder switch cases. That's also why, if we need to continue on another case, we need to explicitly jump (goto) it.

Not having a an explicit break; hinders readbility when cases like the one presented by @gafter come into play.

If I were to equate brain sizes to proposals... never mind.

@XuTpuK
Copy link
Author

XuTpuK commented Mar 3, 2015

@sharwell I see absolutely nothing what explains why we need 'break'. Did you read carefully my proposal? I explained clearly that "FALL THROUGH" is the least necessary thing we need from a simple SWITCH operator. If you repeat something after "well known Lippert" it doesn't make his useless words more meaningful, huh? Just read again proposal and try to explain by yourself WHY we need break. I see no any reason except compatibility with legacy ... java? C?

@XuTpuK
Copy link
Author

XuTpuK commented Mar 3, 2015

<comment deleted>

@XuTpuK
Copy link
Author

XuTpuK commented Mar 3, 2015

@paulomorgado There is nothing about "readability". Let's extend your logic on 'if' operator:

if (somecond) {
    doit();
    // oh, it's so unobvious that 'if' finished! Let's make explicit exit!
    break;// huh, SO MUCH readability! And code is so clean!
} else {
    // yep, "else" also can be considered as "default" in "switch"!
}

So what? Where is readability - I don't see. As well as I don't see ANY reason stating "oh, this 'case' branch is finished, shouldn't we write 10 more lines to say it clear?!".

@sharwell
Copy link
Member

sharwell commented Mar 3, 2015

@XuTpuK You claim that the only point of a switch statement is to identify the single case which needs to run, and then leave the switch statement at the end of that case. However, this is not the case in any of the other frequently used languages containing a switch statement with a syntax similar to C#. In each of the others, the lack of a break statement allows for falling through to the next case. Considering that the combined number of developers with experience in C, C++, and/or Java far exceeds the number of developers familiar with C#, it makes sense that inserting an implicit break statement would result in unexpected bugs in applications developed by entry-level users coming from one of these other languages. Considering a primary point of C# is to help developer productivity and allow them to create higher-quality applications, why would it make sense to silently change familiar patterns for a savings of 6 characters?

@gafter
Copy link
Member

gafter commented Mar 3, 2015

@XuTpuK please keep the comments and feedback professional. We will not tolerate abusive conversations on this repo.

@XuTpuK
Copy link
Author

XuTpuK commented Mar 3, 2015

@sharwell First of all, let's state - we programming on C#. And this language evolves. What's the deal to look at legacy outdated languages like C++/C/Java? Why you don't worry about "6 spaces indent" in Fortran? Just imagine that shock when fortran-guy come and see there is characters before 6-th position!! Same ridiculous is your worry about C++ guys. And hell, dude, C# exist TWELVE YEARS, don't you think it's more than enough to stop worrying about "newcomers from C"?? C# already got its niche and rewards full respect as a primary language for Windows development.
Second argument is about "fall through" trick: definitely it was "cool" at the time of 70-es, when you wrote 1-screen programs. Now it's not "cool" to guess where CPU falls after case. Moreover: C# explicitly prohibits this practice! In other words you speak about supporting hack which even in existing standard considered as error-prone and eliminated! I just offer this: "since 'break' (or any other jump) is mandatory, DON'T WASTE my efforts to write it explicitly!".

I hope my arguments are more than clear.

@XuTpuK
Copy link
Author

XuTpuK commented Mar 3, 2015

<comment deleted>

@gafter
Copy link
Member

gafter commented Mar 3, 2015

While the design of the switch statement may leave much to be desired, it is already in the C# language and familiar to many programmers. Falling through from one case to another is often error-prone, so we disallow it. However, we allow the simple case of fall-through where there are no statements between one case label and the next.

If we were to change the language to say that there is an implicit break after any statements following a case label, that would be a breaking change in the usual case that there is more than one case label in a row. Since we do not want to make breaking changes, we do not want to make the break statement implicit. It would be too subtle to have very different runtime behavior when there are no statements versus there being a single "empty" statement ;.

Having said that, we are considering an alternative switch construct for use with the pattern-matching proposal. The main motivation would be to provide an expression form. Since it would be a distinct syntactic form, we are not constrained by the sensibilities (or otherwise) of the existing switch statement.

Thanks for the question. I hope we've answered it to your satisfaction.

@gafter gafter closed this as completed Mar 3, 2015
@gafter gafter added the Resolution-Answered The question has been answered label Mar 3, 2015
@gafter gafter self-assigned this Mar 3, 2015
@XuTpuK
Copy link
Author

XuTpuK commented Mar 4, 2015

@gafter You understand it wrong. There is NO breaking change:

switch(a) {
case 1:
case 2: // it's just two labels for the same branch
    do_smth();
    // implicit break;
case 3: ; // oops! Empty statement means "it's explicit branch - finish it with 'break' "
case 4: code for case '4'
}

Even if some "mattern-matching switch" will be introduced, it doesn't make life easier with current "switch".

@AnthonyDGreen
Copy link
Contributor

@XuTpuK, your earlier comment was deleted due its rude / offensive nature. Please keep the discussion on this topic professional. If you continue to post comments of this nature we will have to consider more serious actions like banning.

@gafter
Copy link
Member

gafter commented Mar 4, 2015

@XuTpuK I understand precisely. You want two labels with no statement between them to behave fundamentally differently at runtime than two labels with an empty statement between them. That is why I said

It would be too subtle to have very different runtime behavior when there are no statements versus there being a single "empty" statement ;.

It was because I understood that what you want is too subtle for code readability that I closed this issue.

@XuTpuK
Copy link
Author

XuTpuK commented Mar 4, 2015

@gafter Nope, you said I offer "breaking changes". Now you changed your arguments, but they again are inconsistent, because in 99.9999% cases we DO NOT need "empty operator"! Readability (and reliability) will increase since we don't dirt code with unnecessary "breaks" and always make sure we have exactly one branch to execute. Shortly, I ask for a syntactic sugar to skip UNNECESSARY operator "break" in places where it's obvious as 2x2.

@chrisaut
Copy link

chrisaut commented Mar 4, 2015

Readability will certainly not increase, ";" is way too easy to overlook.
Reliability ..... I don't even know what that means, what is more reliable?

C# is not a language for code golf where every character needs to go away. Clear, unambiguous code is way more important. Are you seriously saying that difference between

switch(a) {
case 1:
case 2: 
    do_smth();
}

and

switch(a) {
case 1: ;
case 2: 
    do_smth();
}

is easy to spot at a glance?

gafter did not say you offer breaking changes, he said doing implicit breaks in all cases would be a breaking change (which it is), and doing it your way with a small ";" differentiating the implicit break case from the fall through case would be too subtle.

I suspect you are simply too smart for even the smartest people working on Roslyn to understand. I encourage you to create your own programming language so you don't have to waste your "priceless" time with discussions with us peasants who clearly don't understand.

@XuTpuK
Copy link
Author

XuTpuK commented Mar 4, 2015

@chrisaut See my comment above and appropriate percentage. I don't understand what fun you found in issuing arguments which are nothing, but theoretical ideas behind the beer. From practical POV nobody will need to "skip one value" - most of the cases are "run one of many branches". From this POV we just waste characters to explain compiler what he already knows. Why? Just because somebody's knowledge stoned in 70-es? Then why we meet here SUPPOSING to improve the language?

he said doing implicit breaks in all cases would be a breaking change

SHOW me these cases! I didn't see any.

@XuTpuK
Copy link
Author

XuTpuK commented Mar 4, 2015

BTW for people who forgot history: switch was introduced as a syntax sugar for constructions like:

if (a == 1)
    do1
else if (a == 2)
    do2
...

As you see, not even mention of "fall through" or jump to the case label - these hacks was introduced in C, because C itself is a "high level assembler", which allows any crazy tricks. C# is a high level language, trying to save programmer from mistakes. One of such solutions is prohibition of "fall through" - THAT is why all these breaks become outdated - compiler already assume you always finish branch with some jump. And most used jump is break which of course can be safely skipped.

@chrisaut
Copy link

chrisaut commented Mar 4, 2015

He means if the "break" were implicit in all cases, then it would be a breaking change. This is not your suggestion. It was just a general remark regarding implicit breaks. Of course @gafter himself can confirm if that is indeed what he meant, I don't want to speak for him.

The breaking change is not the argument against this, it's an argument against a different proposal that just came up in this thread and hence was mentioned.

The argument against this is that it is way too easy to overlook and thus hurts readability for the only benefit to save a few keystrokes.

The general consensus seems to be that this is not a worthy tradeoff. You may of course disagree.

People absolutely need to just skip one value, in fact a lot of switch statements around enums do just that. Not most of the time, but it def. does happen. I don't know where you got 99.9999% percent from, I assume you ran an analysis against thousands of solutions? If true it would make a strong argument for your case.

Please do share that data.

@Przemyslaw-W
Copy link

While I really hate XuTpuK's attitude, I tend to agree with him in one
place. I don't think anyone will be forced to write

switch(a) {
case 1: ;
case 2:
do_smth();
}

It should be perfectly fine to explicitly put break there if one needs
to introduce a noop branch:

switch(a) {
case 1:
break;

case 2:
do_smth();
}

It needs to be allowed for backward compatibility reasons. However, when
branch is not empty and there is no explicit break, return, throw or any
other construct required by compiler today, the implicit break could be
inserted. Switch statements tend to be long and implicit break could really
help to limit their verbosity.

I tend to see "break" in switch statements the same way as "return" in the
end of void methods. One can write them if he wants to, but noone is forced
to write them. Now, I know this comparison isn't the best, but this is how
I see this as average programmer.

2015-03-04 11:55 GMT+01:00 XuTpuK notifications@github.com:

@chrisaut https://github.com/chrisaut See my comment above and
appropriate percentage. I don't understand what fun you found in issuing
arguments which are nothing, but theoretical ideas behind the beer. From
practical POV nobody will need to "skip one value" - most of the cases
are "run one of many branches". From this POV we just waste characters to
explain compiler what he already knows. Why? Just because somebody's
knowledge stoned in 70-es? Then why we meet here SUPPOSING to improve the
language?


Reply to this email directly or view it on GitHub
#959 (comment).

@GeirGrusom
Copy link

@XuTpuK It seems you forgot history.

Switch wasn't introduced in C. It already existed in B, and B had more or less taken it directly from BCPL's switchon statement. BCPL took this from ALGOL 60 which had a very strange syntax for the concept, but it existed nonetheless.

edit: sorry this is completely off-topic and does not belong here at all.

@XuTpuK
Copy link
Author

XuTpuK commented Mar 4, 2015

@Przemyslaw-W I hate myself, thanks for support. :)

I tend to see "break" in switch statements the same way as "return" in the
end of void methods

Exactly! While both, we and compiler, know that "case" is finished, we still forced to say "OK, I'm done!". WHY??
And despite fears of others, I declare: it does NOT break any compatibility! "break" still here, but become optional. Switch does NOT allow to "fall through" (and even can reorder branches), so no more fears "one branch runs to another" - we just implicitly limit "one case - one branch" if user forgot to "finish" it.

@AqlaSolutions
Copy link

I have an idea about this:

switch (value)
{
    case 1: //old style
    case 2:
        break;
    case (5) // new style - like catch ... when
    {
        // no break needed but brackets are required
        // it also forces a scope which I think is necessary anyway in lots of cases
        // btw why not allow expression evaluation in conditions?
    }
    case (6) // falling through 
    case (7)
    {
        // ...
    }
    default
    {
        // ...
    }
}

@paulomorgado
Copy link

What's the real benefit over this:

switch (value)
{
    case 1: //old style
    case 2:
        break;
    case 5:
        // break needed but brackets aren't required
        // braces can be used to force a scope
        break;
    case 6: // falling through 
    case 7:
        // ...
        break;
    default
        // ...
        break;
}

@GeirGrusom
Copy link

More consistent with the rest of the language.
This would be try...catch if it followed switch syntax:

try
{

}
catch
{
   WebException ex:
       Log(ex);
       break;
   ArgumentNullException:
       DoFoo();
       break;
   ArgumentException:
   Exception:
       DoBar();
       break;
}

switch does have an odd syntax.

@ibrahimbensalah
Copy link

Take for example the following snippet:

image

The compiler is complaining about missing break statement, thus break is required, thus only one way is supported and thus there is NO AMBIGUITY about symantical meaning, however this syntax is not supported.

I think this is a discussion about style, do we want to be explicit or implicit about break? implicit break could work very fine, however we have to consider if this is not confusing when we already have implicit fall through in case body is empty.

@AqlaSolutions
Copy link

What I proposed still allows to use the current style (with breaks) but also adds a new {brackets} style which, yes, is more consistent with the rest of the language and doesn't need "break".

@ibrahimbensalah I didn't ask to make break implicit. It's still required when using "case ... :" style (but not with "case (...) { ... }").

@whoisj
Copy link

whoisj commented Aug 6, 2015

@AqlaSolutions I like that. It does feel more inline with the rest of the language like that.

If we assume ever case can only address a single statement, much like an if does, then this pans out nicely. However, it would be a breaking change as people tend to put many-many lines of code into a single case, using the break as a kind of "closing brace".

why not allow expression evaluation in conditions?

Why not indeed. It is one of the features of Go that I'm most envious of.

@sharwell
Copy link
Member

sharwell commented Aug 6, 2015

why not allow expression evaluation in conditions?

See the following issues:

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

No branches or pull requests