www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - switch()

reply Manu <turkeyman gmail.com> writes:
So D offers great improvements to switch(), but there are a few small
things I wonder about.

1.
case fall-through is not supported; explicit 'goto case n;' is required.
With this in mind, 'break' is unnecessary. Why is it required? It could be
implicit upon reaching the next case label, or a scope could be used (with
support for omitting the scope for single statements as with if).
It's really noisy, and annoying to write everywhere.

2.
'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a totally different
syntax: 'case 1: .. case 3:'

Why settle on that syntax? The inconsistency looks kinda silly when they
appear together in code.
Surely it's possible to find a syntax that works without repeating case and
':'?

It's also weird, because it seems that 'case n: .. case m:' is inclusive of
m. This may be unexpected.
I'm not sure it's reasonable to use the '..' syntax in this case for that
reason. '..' is an [) range, case ranges must be [] so that it makes sense
when dealing with enum key ranges.

3.
Why is 'default' necessary? If I'm not switching on an enumerated type,
then many values are meaningless. requiring an empty 'default: break;' line
at the end is annoying and noisy.

I often find myself tempted to rewrite blocks of successive if() logic
comparing integers against values/ranges, but it looks silly since the
scope rules are not explicit, and 'default: break;' always wastes an extra
line.
I like to reduce noise in my code, and these switch semantics threaten to
simplify a lot of code, if not for these strange decisions (purely for
legacy compliance?).


Let's consider an example:

Code like this:

int difficulty = -1;
if(e.note.note >= 60 && e.note.note < 72)
difficulty = 0;
else if(e.note.note >= 72 && e.note.note < 84)
difficulty = 1;
else if(e.note.note >= 84 && e.note.note < 96)
difficulty = 2;
else if(e.note.note >= 96 && e.note.note < 108)
difficulty = 3;

The repetition of e.note.note is annoying, and particular choice of
comparisons are liable to result in out-by-ones. It's not nice code to read.


Rewrites like this:

int difficulty;
switch(e.note.note)
{
case 60: .. case 71:
difficulty = 0;
break;
case 72: .. case 83:
difficulty = 1;
break;
case 84: .. case 95:
difficulty = 2;
break;
case 96: .. case 107:
difficulty = 3;
break;
default:
difficulty = -1;
break;
}

That's horrid, it's much longer! And there are pointless wasted lines
everywhere.
The default case is a total waste, since -1 should just be assigned when
initialising the variable above.


We can compact it a bit like this:

int difficulty;
switch(e.note.note)
{
case 60: .. case 71:
difficulty = 0; break;
case 72: .. case 83:
difficulty = 1; break;
case 84: .. case 95:
difficulty = 2; break;
case 96: .. case 107:
difficulty = 3; break;
default:
difficulty = -1; break;
}

But that's horrible too. It's not clear what vertical offset the 'break'
statements shoudl appear at (I hate stacking up multiple statements across
the same line!).
The the default case is still a waste.


Ideally:

int difficulty = -1;
switch(e.note.note)
{
case 60 .. 72:
difficulty = 0;
case 72 .. 84:
difficulty = 1;
case 84 .. 96:
difficulty = 2;
case 96 .. 108:
difficulty = 3;
}

'break's are unnecessary since fallthrough isn't allowed.
Proper numeric range could be supported (assuming [) intervals).
'default' case is unnecessary, and removed.

The switch and braces results in 3 extra lines, but I still feel this level
of simplification results in code that is MUCH more readable than the
sequence of if's I started with. It's super obvious what's going on.

I have quite many blocks like this.

A great man once (actually, frequently) said "If it doesn't look right, it
probably isn't".
Feb 16 2014
next sibling parent reply "Chris Cain" <clcain uncg.edu> writes:
I think the main reason why switches work this way (and 
cannot/should not be changed) is because of one of the main 
design goals of D:

http://dlang.org/overview.html


the same or issue an error.

The proposed changes would violate this, making existing C code 
change meaning silently (wrt implicit "break;" in particular).

TBH, though, I agree that this is somewhat of a speed bump in D. 
I really rarely find myself using switches because of the same 
issues you mention. Unfortunately, without adding new syntax 
(which is also problematic for many reasons), such a thing cannot 
be fixed unless this goal is ignored.
Feb 16 2014
next sibling parent reply "Chris Cain" <clcain uncg.edu> writes:
On Sunday, 16 February 2014 at 16:04:53 UTC, Chris Cain wrote:
 http://dlang.org/overview.html


 behave the same or issue an error.
I feel the need to clarify: I really don't mean all of the proposed changes are bad for this reason, but one of the ones I would find most value in (implicit "break;"s) is inappropriate. Sorry for the double post. I do like the suggestions in spirit. ** I'm also surprised "default" is necessary. I thought it was only necessary in final switches.
Feb 16 2014
parent Manu <turkeyman gmail.com> writes:
On 17 February 2014 02:08, Chris Cain <clcain uncg.edu> wrote:

 ** I'm also surprised "default" is necessary. I thought it was only
 necessary in final switches.
final switch requires you to implement every possible case, so it makes no sense in that case.
Feb 16 2014
prev sibling parent Manu <turkeyman gmail.com> writes:
On 17 February 2014 02:04, Chris Cain <clcain uncg.edu> wrote:

 I think the main reason why switches work this way (and cannot/should not
 be changed) is because of one of the main design goals of D:

 http://dlang.org/overview.html


 or issue an error.

 The proposed changes would violate this, making existing C code change
 meaning silently (wrt implicit "break;" in particular).
Okay, but that doesn't address the range cases... TBH, though, I agree that this is somewhat of a speed bump in D. I really
 rarely find myself using switches because of the same issues you mention.
 Unfortunately, without adding new syntax (which is also problematic for
 many reasons), such a thing cannot be fixed unless this goal is ignored.
I agree, I rarely use switch statements for the same reason. Deliberately gimping a very useful control statement because it's shit in another language (it really is!), seems like an extremely lame design goal to me. switch could be so much more useful with these changes. I suspect most people would avoid it much of the time for these reasons.
Feb 16 2014
prev sibling next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On 2/17/2014 12:42 AM, Manu wrote:
 So D offers great improvements to switch(), but there are a few small
 things I wonder about.

 1.
 case fall-through is not supported; explicit 'goto case n;' is required.
I only get the error when falling through to default, in which case it says: Error: switch case fallthrough - use 'goto default;' if intended Fall-through compiles and runs otherwise. This is something I'm doing right now: switch( event.type ) { // No error on falling through here <<<---------------------. case SDL_KEYDOWN: case SDL_KEYUP: if( _keyHandler ) { _keyHandler( cast( Key )event.key.keysym.scancode, cast( KeyState )event.key.state, cast( KeyModifier )event.key.keysym.mod ); } break; case SDL_WINDOWEVENT: handleWindowEvent( &event.window ); break; case SDL_QUIT: if( _quitHandler ) { _quitHandler(); } break; default: break; }
Feb 16 2014
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 16 Feb 2014 11:37:48 -0500, Mike Parker <aldacron gmail.com> wrote:

 On 2/17/2014 12:42 AM, Manu wrote:
 So D offers great improvements to switch(), but there are a few small
 things I wonder about.

 1.
 case fall-through is not supported; explicit 'goto case n;' is required.
I only get the error when falling through to default, in which case it says: Error: switch case fallthrough - use 'goto default;' if intended Fall-through compiles and runs otherwise. This is something I'm doing right now: switch( event.type ) { // No error on falling through here <<<---------------------. case SDL_KEYDOWN: case SDL_KEYUP:
This is allowed, because there is no code between the case statements. It's the one case where fallthrough is allowed. If you put a statement in between the two, it would complain. -Steve
Feb 16 2014
next sibling parent reply "Casper =?UTF-8?B?RsOmcmdlbWFuZCI=?= <shorttail hotmail.com> writes:
What about new evolved switch statement, called something as to 
not confuse it with C syntax? It could be a simple rewrite thing.

mysteryswitch (some expression) {
   case 1: some statement;
   case 2 .. 4: some other statement;
}

could rewrite to

switch (some expression) {
   case 1: some statement; break;
   case 2: .. case 4: some other statement; break;
   default: break;
}
Feb 16 2014
parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Monday, 17 February 2014 at 00:22:52 UTC, Casper Færgemand 
wrote:
 What about new evolved switch statement, called something as to 
 not confuse it with C syntax? It could be a simple rewrite 
 thing.

 mysteryswitch (some expression) {
   case 1: some statement;
   case 2 .. 4: some other statement;
 }

 could rewrite to

 switch (some expression) {
   case 1: some statement; break;
   case 2: .. case 4: some other statement; break;
   default: break;
 }
I like 'match' as Rust has: ---- match (some expression) { case 1: some statement; case 2 .. 4: some other statement; } ----
Feb 16 2014
parent "Asman01" <jckj33 gmail.com> writes:
On Monday, 17 February 2014 at 00:27:44 UTC, Namespace wrote:
 On Monday, 17 February 2014 at 00:22:52 UTC, Casper Færgemand 
 wrote:
 What about new evolved switch statement, called something as 
 to not confuse it with C syntax? It could be a simple rewrite 
 thing.

 mysteryswitch (some expression) {
  case 1: some statement;
  case 2 .. 4: some other statement;
 }

 could rewrite to

 switch (some expression) {
  case 1: some statement; break;
  case 2: .. case 4: some other statement; break;
  default: break;
 }
I like 'match' as Rust has: ---- match (some expression) { case 1: some statement; case 2 .. 4: some other statement; } ----
+1, I linked this "match". How a name does the difference. I suggest to implement this one instead of. I think that it's very nice because don't make nobody confusing anymore or break D philosofy. some-statement should have an implicit 'break' generated by the compiler just like Pascal cases have (and rust I guess)
Feb 17 2014
prev sibling parent Mike Parker <aldacron gmail.com> writes:
On 2/17/2014 5:57 AM, Steven Schveighoffer wrote:

 This is allowed, because there is no code between the case statements.
 It's the one case where fallthrough is allowed. If you put a statement
 in between the two, it would complain.
That hit me this morning when I woke up. It's the reason I got the error when I fell through to default.
Feb 16 2014
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/16/2014 04:42 PM, Manu wrote:
 So D offers great improvements to switch(), but there are a few small
 things I wonder about.

 1.
 case fall-through is not supported; explicit 'goto case n;' is required.
Yes it is supported. Use 'goto case;'.
 With this in mind, 'break' is unnecessary.
Sure.
 Why is it required?
Backwards compatibility.
 It could be implicit upon reaching the next case label, or a scope could be
used
 (with support for omitting the scope for single statements as with if).
 It's really noisy, and annoying to write everywhere.
 ...
Like this: http://ceylon-lang.org/documentation/reference/statement/switch/ ?
 2.
 'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a totally
 different syntax: 'case 1: .. case 3:'

 Why settle on that syntax? The inconsistency looks kinda silly when they
 appear together in code.
 Surely it's possible to find a syntax that works without repeating case
 and ':'?
 ...
AFAIK it is to emphasize that the range is inclusive, as opposed to: case 1..3: IIRC Walter's intention was to format it as follows: switch(x){ case 1: .. case 3: }
 It's also weird, because it seems that 'case n: .. case m:' is inclusive
 of m. This may be unexpected.
 I'm not sure it's reasonable to use the '..' syntax in this case for
 that reason. '..' is an [) range, case ranges must be [] so that it
 makes sense when dealing with enum key ranges.
 ...
Sure, mixing case lists and ranges in a single statement would be neat, but what would be your preferred syntax?
 3.
 Why is 'default' necessary? If I'm not switching on an enumerated type,
 then many values are meaningless. requiring an empty 'default: break;'
 line at the end is annoying and noisy.
 ...
There is more than one sensible default, so being explicit about the default makes sense.
 I often find myself tempted to rewrite blocks of successive if() logic
 comparing integers against values/ranges, but it looks silly since the
 scope rules are not explicit, and 'default: break;' always wastes an
 extra line.
 I like to reduce noise in my code, and these switch semantics threaten
 to simplify a lot of code, if not for these strange decisions (purely
 for legacy compliance?).


 Let's consider an example:

 Code like this:

 int difficulty = -1;
 if(e.note.note >= 60 && e.note.note < 72)
     difficulty = 0;
 else if(e.note.note >= 72 && e.note.note < 84)
     difficulty = 1;
 else if(e.note.note >= 84 && e.note.note < 96)
     difficulty = 2;
 else if(e.note.note >= 96 && e.note.note < 108)
     difficulty = 3;

 The repetition of e.note.note is annoying, and particular choice of
 comparisons are liable to result in out-by-ones. It's not nice code to read.


 Rewrites like this:

 int difficulty;
 switch(e.note.note)
 {
     case 60: .. case 71:
         difficulty = 0;
     break;
     case 72: .. case 83:
         difficulty = 1;
     break;
     case 84: .. case 95:
         difficulty = 2;
     break;
     case 96: .. case 107:
         difficulty = 3;
     break;
     default:
         difficulty = -1;
     break;
 }

 That's horrid, it's much longer! And there are pointless wasted lines
 everywhere.
The wasted lines are due to your formatting. int difficulty=-1; switch(e.note.note){ case 60: .. case 71: difficulty = 0; break; case 72: .. case 83: difficulty = 1; break; case 84: .. case 95: difficulty = 2; break; case 96: .. case 107: difficulty = 3; break; default: break; } Of course, I'd just write the above as: int difficulty = e.note.note.between(60,108) ? (e.note.note-60)/12 : -1;
 The default case is a total waste, since -1 should just be assigned when
 initialising the variable above.
 ...
How is the compiler supposed to know? It might be a logic error for e.note.note to be out of range.
...

 Ideally:

 int difficulty = -1;
 switch(e.note.note)
 {
     case 60 .. 72:
         difficulty = 0;
     case 72 .. 84:
         difficulty = 1;
     case 84 .. 96:
         difficulty = 2;
     case 96 .. 108:
         difficulty = 3;
 }
 ...
Ideally closer to: int difficulty = switch(e.note.note){ 60 .. 72 => 0; 73 .. 84 => 1; 84 .. 96 => 2; 96 .. 108 => 3; _ => -1; };
Feb 16 2014
parent reply Manu <turkeyman gmail.com> writes:
On 17 February 2014 03:14, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 02/16/2014 04:42 PM, Manu wrote:

 So D offers great improvements to switch(), but there are a few small
 things I wonder about.

 1.
 case fall-through is not supported; explicit 'goto case n;' is required.
Yes it is supported. Use 'goto case;'.
That's still entirely explicit. It's not fallthrough. It could be implicit upon reaching the next case label, or a scope could
 be used
 (with support for omitting the scope for single statements as with if).
 It's really noisy, and annoying to write everywhere.
 ...
Like this: http://ceylon-lang.org/documentation/reference/ statement/switch/ ?
Yes. That's just common sense, right? 2.
 'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a totally
 different syntax: 'case 1: .. case 3:'

 Why settle on that syntax? The inconsistency looks kinda silly when they
 appear together in code.
 Surely it's possible to find a syntax that works without repeating case
 and ':'?
 ...
AFAIK it is to emphasize that the range is inclusive, as opposed to: case 1..3: IIRC Walter's intention was to format it as follows: switch(x){ case 1: .. case 3: }
Right. Inclusive ranges are intended for enum ranges though. Integer ranges are just fine in the usual way. I'd support both... It's also weird, because it seems that 'case n: .. case m:' is inclusive
 of m. This may be unexpected.
 I'm not sure it's reasonable to use the '..' syntax in this case for
 that reason. '..' is an [) range, case ranges must be [] so that it
 makes sense when dealing with enum key ranges.
 ...
Sure, mixing case lists and ranges in a single statement would be neat, but what would be your preferred syntax?
case 1, 2, 5..10, 20: But that's not what I actually meant. I was actually talking about the case when 'case 1,2,5:' appears in the same switch() block as 'case 10: .. case 19:', when they appear next to eachother, it just looks awkward; like they don't belong together. 3.
 Why is 'default' necessary? If I'm not switching on an enumerated type,
 then many values are meaningless. requiring an empty 'default: break;'
 line at the end is annoying and noisy.
 ...
There is more than one sensible default, so being explicit about the default makes sense.
Don't agree. Most of my cases are such that no default action is required. It's just noise in my code. If I fail to handle default when I probably should have, then that's clearly my mistake. The wasted lines are due to your formatting.
 int difficulty=-1;

 switch(e.note.note){
     case 60: .. case 71:  difficulty = 0; break;
     case 72: .. case 83:  difficulty = 1; break;
     case 84: .. case 95:  difficulty = 2; break;
     case 96: .. case 107: difficulty = 3; break;
     default: break;
 }
I hate this. It violates the formatting conventions used EVERYWHERE else. In terms of formatting, it doesn't even look like the same language. Of course, I'd just write the above as:
 int difficulty = e.note.note.between(60,108) ? (e.note.note-60)/12 : -1;
Yes yes, very clever. Obviously it's an example and could come in any shape or form. Personally, I also wouldn't do that anyway; basic readability has definitely been lost. The default case is a total waste, since -1 should just be assigned when
 initialising the variable above.
 ...
How is the compiler supposed to know? It might be a logic error for e.note.note to be out of range.
Then I should have used an invariant or something actually designed for catching values out of range. It's not the compilers fault I made a logic error. I could make a logic error literally anywhere in my code. Why force an annoying rule to maybe sometimes catch exactly one case (while making other competing cases where a logic error isn't present more annoying).
Feb 16 2014
next sibling parent reply "Chris Cain" <clcain uncg.edu> writes:
On Monday, 17 February 2014 at 04:00:08 UTC, Manu wrote:
 Of course, I'd just write the above as:
 int difficulty = e.note.note.between(60,108) ? 
 (e.note.note-60)/12 : -1;
Yes yes, very clever. Obviously it's an example and could come in any shape or form. Personally, I also wouldn't do that anyway; basic readability has definitely been lost.
I know I'm weird here, but I disagree. I had no idea what you were doing at the beginning of the topic, but that one line revealed to me that this is likely related to the music game thing you proposed awhile ago. I can't really explain why "note" was insufficient before to reveal that, but once I saw "12" explicitly on its own I immediately thought ~"12 ... semitones ... octaves ... note ... it's music ... he was working on a music game ... he came up with this while working on the music game". So, it's classifying difficulty based on which octave a note is for your music game? Of course, I might be wrong here. It just seems to me that the reducing of information made it clearer what the purpose is disregarding the other contextual information that probably exists outside of what you're trying to show us.
Feb 17 2014
parent Manu <turkeyman gmail.com> writes:
On 17 February 2014 22:38, Chris Cain <clcain uncg.edu> wrote:

 On Monday, 17 February 2014 at 04:00:08 UTC, Manu wrote:

 Of course, I'd just write the above as:

 int difficulty = e.note.note.between(60,108) ? (e.note.note-60)/12 : -1;
Yes yes, very clever. Obviously it's an example and could come in any shape or form. Personally, I also wouldn't do that anyway; basic readability has definitely been lost.
I know I'm weird here, but I disagree. I had no idea what you were doing at the beginning of the topic, but that one line revealed to me that this is likely related to the music game thing you proposed awhile ago. I can't really explain why "note" was insufficient before to reveal that, but once I saw "12" explicitly on its own I immediately thought ~"12 ... semitones ... octaves ... note ... it's music ... he was working on a music game ... he came up with this while working on the music game". So, it's classifying difficulty based on which octave a note is for your music game?
I'm dealing with arbitrary values as appearing in midi files. There is a relationship that you made, kind of... but it's incidental. It's actually an abuse of the structure of a midi file to relate concepts to octaves; designers use the separation of octaves in midi composition software to separate some arbitrary data, because octaves often provide good visual separation in editors, making it easier to visualise the data they're authoring. It's also just the one that was on my screen when I cut and pasted. There are lots of others that lead me to consider this as a frequently recurring pattern and become annoyed that my code was was uniformly ugly. Of course, I might be wrong here. It just seems to me that the reducing of
 information made it clearer what the purpose is disregarding the other
 contextual information that probably exists outside of what you're trying
 to show us.
The purpose is kinda unrelated conceptually. In reality, what has happened is that you have become confused. You were searching for reason where there is none, and the algorithmic expression lead you to a false conclusion :)
Feb 17 2014
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/17/2014 04:59 AM, Manu wrote:
 On 17 February 2014 03:14, Timon Gehr <timon.gehr gmx.ch
 ...

     Of course, I'd just write the above as:

     int difficulty = e.note.note.between(60,108) ? (e.note.note-60)/12 : -1;


 Yes yes, very clever.
I can't agree.
 Obviously it's an example and could come in any shape or form.
Obviously. Hence, no need to point this out.
 Personally, I also wouldn't do that anyway; basic readability has
 definitely been lost.
 ...
IMO quite the opposite is true. And everything around it will tend to become simpler to follow at a glance as well.
         The default case is a total waste, since -1 should just be
         assigned when
         initialising the variable above.
         ...


     How is the compiler supposed to know? It might be a logic error for
     e.note.note to be out of range.


 Then I should have used an invariant or something actually designed for
 catching values out of range.
 It's not the compilers fault I made a logic error. I could make a logic
 error literally anywhere in my code. Why force an annoying rule
It's not inherent to the rule.
 to maybe sometimes catch exactly one case
Well, it is also not the kind of error I make, but it still documents intention. It would be easy to sloppily omit default: assert(0) without this rule without actually affecting correct code, but I wouldn't warmly recommend this practice.
 (while making other competing cases where a logic error isn't present more
annoying).
That's roughly the dynamic typing argument. Obviously 'switch' is quite lame (being lame seems to be an inherent quality of statement-based syntax), but the requirement to explicitly handle the default case instead of implicitly skipping it is not why.
Feb 17 2014
prev sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/16/2014 10:59 PM, Manu wrote:
 On 17 February 2014 03:14, Timon Gehr <timon.gehr gmx.ch> wrote:
 int difficulty=-1;

 switch(e.note.note){
      case 60: .. case 71:  difficulty = 0; break;
      case 72: .. case 83:  difficulty = 1; break;
      case 84: .. case 95:  difficulty = 2; break;
      case 96: .. case 107: difficulty = 3; break;
      default: break;
 }
I hate this. It violates the formatting conventions used EVERYWHERE else. In terms of formatting, it doesn't even look like the same language.
I find it extremely easy to read, and much nicer than the other options. Being able to omit break and use switch as an expression would be a little nicer still, but I still fail to see why the above, no matter how it's formatted, is so terribly intolerable.
Feb 17 2014
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/16/2014 7:42 AM, Manu wrote:
 1. case fall-through is not supported; explicit 'goto case n;' is required.
 With this in mind, 'break' is unnecessary. Why is it required?
It's that way to prevent confusion from people used to C/C++, and/or transliterating code from such to D.
 2. 'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a totally different
 syntax: 'case 1: .. case 3:'

 Why settle on that syntax? The inconsistency looks kinda silly when they appear
 together in code.
 Surely it's possible to find a syntax that works without repeating case and
':'?
Many syntaxes were proposed for that, and this was the best one.
 I'm not sure it's reasonable to use the '..' syntax in this case for that
 reason. '..' is an [) range, case ranges must be [] so that it makes sense when
 dealing with enum key ranges.
That was the known problem with using a..b
 3. Why is 'default' necessary? If I'm not switching on an enumerated type, then
 many values are meaningless. requiring an empty 'default: break;' line at the
 end is annoying and noisy.
It originally was not required, but there was a campaign by a lot of D users to make it required to deal with the common bug of adding a value in one switch statement but forgetting to add it to another corresponding one.
 if not for these strange decisions (purely for legacy compliance?).
I hope you'll find them less strange now.
Feb 16 2014
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-02-16 21:03, Walter Bright wrote:

 It originally was not required, but there was a campaign by a lot of D
 users to make it required to deal with the common bug of adding a value
 in one switch statement but forgetting to add it to another
 corresponding one.
Isn't that what final switches are for? Or are default statements not allowed in final switches at all? -- /Jacob Carlborg
Feb 16 2014
parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/16/2014 12:06 PM, Jacob Carlborg wrote:
 On 2014-02-16 21:03, Walter Bright wrote:

 It originally was not required, but there was a campaign by a lot of D
 users to make it required to deal with the common bug of adding a value
 in one switch statement but forgetting to add it to another
 corresponding one.
Isn't that what final switches are for?
Final switches only help for enums.
 Or are default statements not allowed in final switches at all?
Right, they aren't.
Feb 16 2014
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On 17 February 2014 06:03, Walter Bright <newshound2 digitalmars.com> wrote:

 On 2/16/2014 7:42 AM, Manu wrote:

 1. case fall-through is not supported; explicit 'goto case n;' is
 required.
 With this in mind, 'break' is unnecessary. Why is it required?
It's that way to prevent confusion from people used to C/C++, and/or transliterating code from such to D.
I figured that, but deliberately gimping a construct that could be far more useful than it is, just because it was crap in some other language seems like a poor choice to me. 2. 'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a totally
 different
 syntax: 'case 1: .. case 3:'

 Why settle on that syntax? The inconsistency looks kinda silly when they
 appear
 together in code.
 Surely it's possible to find a syntax that works without repeating case
 and ':'?
Many syntaxes were proposed for that, and this was the best one. I'm not sure it's reasonable to use the '..' syntax in this case for that
 reason. '..' is an [) range, case ranges must be [] so that it makes
 sense when
 dealing with enum key ranges.
That was the known problem with using a..b
Yeah, but when I suggest a..b, I suggest applying existing [) range rules, as would be expected. It doesn't make sense for enum keys, and I think the existing syntax is acceptable for [] usage with enum keys, but you're not always switching on enums. Perhaps supporting both would be useful, where a..b is [) as expected, and 'case A: .. case B:' is [] for use with enums? 3. Why is 'default' necessary? If I'm not switching on an enumerated type,
 then
 many values are meaningless. requiring an empty 'default: break;' line at
 the
 end is annoying and noisy.
It originally was not required, but there was a campaign by a lot of D users to make it required to deal with the common bug of adding a value in one switch statement but forgetting to add it to another corresponding one.
Isn't that the entire point of final switch? Why introduce final switch to address that, then do this aswell? if not for these strange decisions (purely for legacy compliance?).

 I hope you'll find them less strange now.
Well, I get them, but it seems like an awful lot of missed opportunities. D threatens to improve substantially on C switch, but it only goes half way, resulting in something that's only slightly nicer if used precisely in the 'C way'. It doesn't offer any new use cases, which is only really inhibited by some quite trivial decisions. Maybe a new and improved syntax for D that can be lowered? 'select()'? 'match()'? I would set it up with proper scoping syntax: match([x =] expression) { case(1, 3, 4, 6) // use parentheses like all other control statements? { proper scope block... goto named_case; } case(10..20) // [) interval singleStatement(); case(2, 5, 7..10) // this would be handy too singleStatement(); case named_case // it's a useful concept; explicit named case for shared work, which others can jump to. not sure how best to express it... sharedOperation(); default { // surely this should be optional? } } I think a really useful construct could be made out of switch, but it seems that it won't happen because it must support C code unchanged.
Feb 16 2014
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/16/2014 7:39 PM, Manu wrote:
 I think a really useful construct could be made out of switch, but it seems
that
 it won't happen because it must support C code unchanged.
I tend to agree with Andrei on this - the proposals aren't fundamental or game changing, and are kinda just bouncing the rubble around (po-tay-to vs po-tah-to).
 Isn't that the entire point of final switch?
 Why introduce final switch to address that, then do this aswell?
Implicit in your questions is switches will only be used on enums, i.e. all the values a type can be are known to the compiler. This is only true for a smallish subset of types that are switched on. final switch : for enums default : for everything else
Feb 17 2014
parent reply Manu <turkeyman gmail.com> writes:
On 17 February 2014 18:43, Walter Bright <newshound2 digitalmars.com> wrote:

 On 2/16/2014 7:39 PM, Manu wrote:

 I think a really useful construct could be made out of switch, but it
 seems that
 it won't happen because it must support C code unchanged.
I tend to agree with Andrei on this - the proposals aren't fundamental or game changing, and are kinda just bouncing the rubble around (po-tay-to vs po-tah-to).
Refer to my other reply wrt the 'rubble' concept. I think a quality implementation would be fairly game changing. A properly scoped and fully featured switch/select/match statement would result in some radical simplifications of code all over the place. Sequential if/if else statements might see a decline, and there would be far less repetition of terms in the conditional statements. If switch (or something like it) were massaged to be as nice to use as foreach is, I think you'll find it will be used all over the place. Note: I probably wouldn't be exaggerating if I said foreach is the main reason I started looking into D in the first place. When I first glanced over the D feature list, I could have easily closed the browser tab, but that's the thing that kept me reading... I've said it before, and I'll say it again, I think these things are _so fundamental_, that their importance is often underestimated, and often overlooked. Could you imagine D without foreach? In retrospect, you can't. But If you were arguing from a position where you never had anything like foreach to the value of it's inclusion, it wouldn't look anywhere near as strong as it does in retrospect. The idea of eliminating many unsightly if/else if blocks is very appealing to me. Just scanning through my code, I can imagine so many instances where my code would become more readable. People usually use switch today for the same reason, but it's always a hard call... is the crappiness of 'switch' a quantifiable improvement over the if/else if sequence? I find myself asking that question all the time. That's why I brought it up.
 Isn't that the entire point of final switch?
 Why introduce final switch to address that, then do this aswell?
Implicit in your questions is switches will only be used on enums, i.e. all the values a type can be are known to the compiler. This is only true for a smallish subset of types that are switched on. final switch : for enums default : for everything else
But... that's like making it an error for an if() to appear without an else statement. It's _exactly_ like that. I don't think I couldn't disagree more. Imagine: if(condition) { doSomething(); } else {} blah blah. if(failed) { handleError(); } else {}
Feb 17 2014
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 5:49 AM, Manu wrote:
 Refer to my other reply wrt the 'rubble' concept.
Sure :-)
 I think a quality implementation would be fairly game changing. A properly
 scoped and fully featured switch/select/match statement would result in some
 radical simplifications of code all over the place.
I have a real hard time seeing the proposed changes as radical or game changing.
 If switch (or something like it) were massaged to be as nice to use as foreach
 is, I think you'll find it will be used all over the place.
foreach() is quite a large improvement, because by abstracting away the mechanics of iteration, code need no longer be aware of just what abstract type is being looped over. Furthermore, it eliminates several common sources of coding bugs. I'm not seeing this with changes in switch.
Feb 17 2014
parent Manu <turkeyman gmail.com> writes:
On 18 February 2014 05:51, Walter Bright <newshound2 digitalmars.com> wrote:

 On 2/17/2014 5:49 AM, Manu wrote:

 Refer to my other reply wrt the 'rubble' concept.
Sure :-) I think a quality implementation would be fairly game changing. A properly
 scoped and fully featured switch/select/match statement would result in
 some
 radical simplifications of code all over the place.
I have a real hard time seeing the proposed changes as radical or game changing. If switch (or something like it) were massaged to be as nice to use as
 foreach
 is, I think you'll find it will be used all over the place.
foreach() is quite a large improvement, because by abstracting away the mechanics of iteration, code need no longer be aware of just what abstract type is being looped over. Furthermore, it eliminates several common sources of coding bugs. I'm not seeing this with changes in switch.
I think abstracting away volumes of comparative matching logic is similarly interesting to eliminating tedious iteration logic. I find out-by-ones and bogus comparisons that I didn't spot is a very common class of bug (often just because matching code is long and repetitive). Cut and paste, oops; you didn't spot that <= is actually >= from where you cut... or you didn't see it because the line is so long with repetition of the terms being compared that it's run off the right edge. *disclaimer: peoples experiences may differ, this is my experience. I agree, it's probably not as game-changing as foreach, but I think if you had it for a few years and became accustomed to writing much of that comparative logic in a simple and really obvious/less-repetitive way, you'd look back and wonder how you did without it. I suspect it's possible that the repetitive matching logic often found in if() statements represent a lot more code by volume than the iteration logic eliminated by foreach ever did in for loops. I can't say how big a deal it is until I make a habit of applying it everywhere, but I can imagine the potential for massive improvement in readability, and elimination of many long if() expressions. Comparing the savings: for(int i=0; i<things.length; ++i) -> foreach(i, thing; things) if(thing.member >= min && thing.member < max) -> match(thing.member) case(min..max) If an 'else if' appears, the savings compound considerably. I'm a practicality motivated programmer, I don't dream about futuristic features as much as I respond to the things that annoy me consistently on a daily basis. I don't like friction between me and getting my work done, and it's the little things that come up most often that tend to really bug me. I'd like to think the languages foundational keywords should offer a good experience. You'll be typing them over and over again from now until the day you die. Looking at other languages, I think there's clearly a missed opportunity to do something a lot more modern and useful with the switch concept. Rust does it nice. It's got a nice functional flavour to it.
Feb 17 2014
prev sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/16/2014 10:39 PM, Manu wrote:
 Yeah, but when I suggest a..b, I suggest applying existing [) range rules,
 as would be expected.

 It doesn't make sense for enum keys, and I think the existing syntax is
 acceptable for [] usage with enum keys, but you're not always switching on
 enums.
 Perhaps supporting both would be useful, where a..b is [) as expected, and
 'case A: .. case B:' is [] for use with enums?
There was a HUGE debate on all this back when the feature was first added, and every miniscule possibility and detail was thoroughly examined and argued ad nauseam. IIRC, I was actually one the people not real happy with the syntax we have, but the thing is, it really isn't a big deal. It's just syntactical details on one particular construct. Yea, switch could be nicer looking, and it's not one of the nicer parts of D syntax, but the semantics are reasonably solid and that's the important part. The rest is just details and bikeshedding. Personally, I would LOVE to see Nemerle's match statement/expression get into D, but other than that, out of all the tasks D faces, the switch syntax is pretty bottom-rung on both the importance and significance scales.
Feb 17 2014
prev sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
On 2/16/14, 5:03 PM, Walter Bright wrote:
 On 2/16/2014 7:42 AM, Manu wrote:
 3. Why is 'default' necessary? If I'm not switching on an enumerated
 type, then
 many values are meaningless. requiring an empty 'default: break;' line
 at the
 end is annoying and noisy.
It originally was not required, but there was a campaign by a lot of D users to make it required to deal with the common bug of adding a value in one switch statement but forgetting to add it to another corresponding one.
Could you show an example of such scenario? I don't get it.
Feb 17 2014
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 5:48 AM, Ary Borenszweig wrote:
 On 2/16/14, 5:03 PM, Walter Bright wrote:
 It originally was not required, but there was a campaign by a lot of D
 users to make it required to deal with the common bug of adding a value
 in one switch statement but forgetting to add it to another
 corresponding one.
Could you show an example of such scenario? I don't get it.
Having a set of bit flags, and adding another bit flag later, and failing to account for that in existing switch statements.
Feb 17 2014
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
On 2/17/14, 7:31 PM, Walter Bright wrote:
 On 2/17/2014 5:48 AM, Ary Borenszweig wrote:
 On 2/16/14, 5:03 PM, Walter Bright wrote:
 It originally was not required, but there was a campaign by a lot of D
 users to make it required to deal with the common bug of adding a value
 in one switch statement but forgetting to add it to another
 corresponding one.
Could you show an example of such scenario? I don't get it.
Having a set of bit flags, and adding another bit flag later, and failing to account for that in existing switch statements.
auto bit_flag = ...; switch(bit_flag) { case old_bit_flag_1: // do something break; case old_bit_flag_2: // do something break; default: break; } Now suppose bit_flag can get an additional "new_bit_flag" value. How does "default" helps me notice that I'm supposed to add it to that switch statement? I still can't see your point :-( Maybe I need some more example code to understand it.
Feb 17 2014
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 2:43 PM, Ary Borenszweig wrote:
 Now suppose bit_flag can get an additional "new_bit_flag" value. How does
 "default" helps me notice that I'm supposed to add it to that switch statement?
Because if you account for all the cases, you write: default: assert(0); Now you intentionally tell the user that you intentionally covered all the cases.
Feb 17 2014
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 17 Feb 2014 18:01:38 -0500, Walter Bright  
<newshound2 digitalmars.com> wrote:

 On 2/17/2014 2:43 PM, Ary Borenszweig wrote:
 Now suppose bit_flag can get an additional "new_bit_flag" value. How  
 does
 "default" helps me notice that I'm supposed to add it to that switch  
 statement?
Because if you account for all the cases, you write: default: assert(0); Now you intentionally tell the user that you intentionally covered all the cases.
Would it not be better to infer this, and you could override it by doing default: break;? If that's the "right way", then it should be the default way. I agree with Ary. Sequence will go as follows: Programmer: switch(x) { case 1: statement; break; } Compiler: no no no, you need a default case! Programmer: "Oh fine!" switch(x) { case 1: statement; break; default: break; } How is this advantageous? It just seems annoying... -Steve
Feb 17 2014
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 6:17 PM, Steven Schveighoffer wrote:
 How is this advantageous? It just seems annoying...
Because it makes the programmer's intent clear - are all the cases accounted for, or are there defaults? Of course, no compiler can make you write correct code. But if you're going to write a default anyway, odds are you'll choose the right one.
Feb 17 2014
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 17 Feb 2014 22:53:15 -0500, Walter Bright  
<newshound2 digitalmars.com> wrote:

 On 2/17/2014 6:17 PM, Steven Schveighoffer wrote:
 How is this advantageous? It just seems annoying...
Because it makes the programmer's intent clear - are all the cases accounted for, or are there defaults? Of course, no compiler can make you write correct code. But if you're going to write a default anyway, odds are you'll choose the right one.
I think your anecdotal experience with exception specification in Java is at odds with this expectation. We all know programmers who are faced with seemingly annoyance hoops to jump through jump through them with the least possible effort. -Steve
Feb 18 2014
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
On 2/18/14, 11:56 AM, Steven Schveighoffer wrote:
 On Mon, 17 Feb 2014 22:53:15 -0500, Walter Bright
 <newshound2 digitalmars.com> wrote:

 On 2/17/2014 6:17 PM, Steven Schveighoffer wrote:
 How is this advantageous? It just seems annoying...
Because it makes the programmer's intent clear - are all the cases accounted for, or are there defaults? Of course, no compiler can make you write correct code. But if you're going to write a default anyway, odds are you'll choose the right one.
I think your anecdotal experience with exception specification in Java is at odds with this expectation. We all know programmers who are faced with seemingly annoyance hoops to jump through jump through them with the least possible effort. -Steve
Exactly. Programmers will just put "default: break" because of this annoyance without thinking too much if it should be this or assert(0). I think that "final switch" should have the function of checking that you covered all cases, be it with a default case or not.
Feb 18 2014
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/18/2014 7:45 AM, Ary Borenszweig wrote:
 I think that "final switch" should have the function of checking that you
 covered all cases,
That's just what it does.
 be it with a default case or not.
final is meaningless with a default.
Feb 18 2014
prev sibling parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 18 February 2014 at 15:45:31 UTC, Ary Borenszweig 
wrote:
 Exactly. Programmers will just put "default: break" because of 
 this annoyance without thinking too much if it should be this 
 or assert(0).

 I think that "final switch" should have the function of 
 checking that you covered all cases, be it with a default case 
 or not.
A better warning/error message may help here. Please have a look at this PR: https://github.com/D-Programming-Language/dmd/pull/3287
Feb 19 2014
prev sibling parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Steven Schveighoffer"  wrote in message 
news:op.xbhfr5kreav7ka stevens-macbook-pro.local...

 Of course, no compiler can make you write correct code. But if you're 
 going to write a default anyway, odds are you'll choose the right one.
I think your anecdotal experience with exception specification in Java is at odds with this expectation. We all know programmers who are faced with seemingly annoyance hoops to jump through jump through them with the least possible effort.
It's not really the same, because silencing checked exceptions results in a solution that is worse than not having checked exceptions at all. Here if the programmer takes the 'easy route' and sticks in a "default: break;" they're just getting the old behavior back.
Feb 18 2014
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 18 Feb 2014 11:11:36 -0500, Daniel Murphy  
<yebbliesnospam gmail.com> wrote:

 "Steven Schveighoffer"  wrote in message  
 news:op.xbhfr5kreav7ka stevens-macbook-pro.local...

 Of course, no compiler can make you write correct code. But if you're  
 going to write a default anyway, odds are you'll choose the right one.
I think your anecdotal experience with exception specification in Java is at odds with this expectation. We all know programmers who are faced with seemingly annoyance hoops to jump through jump through them with the least possible effort.
It's not really the same, because silencing checked exceptions results in a solution that is worse than not having checked exceptions at all. Here if the programmer takes the 'easy route' and sticks in a "default: break;" they're just getting the old behavior back.
My point though, is that the change to require default gains you nothing except annoyed programmers. Why put it in? I see your point that the difference between ignored exceptions and pass-through exceptions is a lot different than breaking by default on a switch statement. But I wasn't trying to make that comparison, just using it as an example of what programmers do. The comparison I AM making is that we are implementation a requirement that will not achieve the behavior goal it sets out to achieve. -Steve
Feb 18 2014
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 18 Feb 2014 11:38:41 -0500, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 The comparison I AM making is that we are implementation a requirement  
 that will not achieve the behavior goal it sets out to achieve.
*implementing*, not implementation :) -Steve
Feb 18 2014
prev sibling next sibling parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Steven Schveighoffer"  wrote in message 
news:op.xbhkirppeav7ka stevens-macbook-pro.local...

 My point though, is that the change to require default gains you nothing 
 except annoyed programmers. Why put it in?
It only gains you nothing if you respond to the error by mindlessly putting a default: break; in. The compiler is trying to help you by getting you to take an extra second and explicitly state what you what. If you are automatically silencing the error without thinking about the semantics you want, you absolutely should be irritated, but the compiler is not the one doing something stupid here. The awful part of checked exceptions comes from two place IMO - the ide gives you a very very easy way to do thing wrong thing (two clicks IIRC), and doing the right thing is often very very difficult (change exception lists on every calling functions). Here the right and wrong choice are about equal difficulty - trivial. I think complaints about typing those extra couple dozen keystrokes are on the same level as "why do I have to put these ';'s everywhere" and "'immutable' has too many letters".
Feb 18 2014
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 18 Feb 2014 12:26:36 -0500, Daniel Murphy  
<yebbliesnospam gmail.com> wrote:

 "Steven Schveighoffer"  wrote in message  
 news:op.xbhkirppeav7ka stevens-macbook-pro.local...

 My point though, is that the change to require default gains you  
 nothing except annoyed programmers. Why put it in?
It only gains you nothing if you respond to the error by mindlessly putting a default: break; in.
It may not be mindless. Most people who want to handle the default case do it. It's usually not so much "I didn't think of handling other values," it's more "I never expect other values to come in, go away annoying compiler error."
 Here the right and wrong choice are about equal difficulty - trivial.  I  
 think complaints about typing those extra couple dozen keystrokes are on  
 the same level as "why do I have to put these ';'s everywhere" and  
 "'immutable' has too many letters".
I think it's the same as saying you have to always have an else clause after an if statement, even if it's "else {}" -Steve
Feb 18 2014
next sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
On 2/18/14, 2:52 PM, Steven Schveighoffer wrote:

 I think it's the same as saying you have to always have an else clause
 after an if statement, even if it's "else {}"
Lol, I swear I didn't read your answer before posting mine.
Feb 18 2014
prev sibling parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Steven Schveighoffer"  wrote in message 
news:op.xbhnw5rbeav7ka stevens-macbook-pro.local...

 It may not be mindless. Most people who want to handle the default case do 
 it. It's usually not so much "I didn't think of handling other values," 
 it's more "I never expect other values to come in, go away annoying 
 compiler error."
So why not put an assert(0) in instead of a break? Tell the compiler that you're assuming there are no other possible values. This is obviously the right thing to do here, and even if you ignore it the compiler _is_ trying to help you.
 I think it's the same as saying you have to always have an else clause 
 after an if statement, even if it's "else {}"
If 'if' was primarily used for the same thing that switch is, this would be perfectly reasonable.
Feb 19 2014
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
On 2/19/14, 6:42 AM, Daniel Murphy wrote:
 "Steven Schveighoffer"  wrote in message
 news:op.xbhnw5rbeav7ka stevens-macbook-pro.local...

 It may not be mindless. Most people who want to handle the default
 case do it. It's usually not so much "I didn't think of handling other
 values," it's more "I never expect other values to come in, go away
 annoying compiler error."
So why not put an assert(0) in instead of a break? Tell the compiler that you're assuming there are no other possible values. This is obviously the right thing to do here, and even if you ignore it the compiler _is_ trying to help you.
Sometimes you don't care about other values, which is different than not expecting other values. For example: auto a = ...; switch(a) { case 1: a += 1; case 2: a += 2; // In other cases, leave "a" as is }
Feb 19 2014
parent reply "QAston" <qaston gmail.com> writes:
On Wednesday, 19 February 2014 at 14:14:01 UTC, Ary Borenszweig 
wrote:
 On 2/19/14, 6:42 AM, Daniel Murphy wrote:
 "Steven Schveighoffer"  wrote in message
 news:op.xbhnw5rbeav7ka stevens-macbook-pro.local...

 It may not be mindless. Most people who want to handle the 
 default
 case do it. It's usually not so much "I didn't think of 
 handling other
 values," it's more "I never expect other values to come in, 
 go away
 annoying compiler error."
So why not put an assert(0) in instead of a break? Tell the compiler that you're assuming there are no other possible values. This is obviously the right thing to do here, and even if you ignore it the compiler _is_ trying to help you.
Sometimes you don't care about other values, which is different than not expecting other values. For example: auto a = ...; switch(a) { case 1: a += 1; case 2: a += 2; // In other cases, leave "a" as is }
Just put default: break instead of that comment, it's shorter that way:P
Feb 19 2014
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 19 Feb 2014 09:17:58 -0500, QAston <qaston gmail.com> wrote:

 On Wednesday, 19 February 2014 at 14:14:01 UTC, Ary Borenszweig wrote:
 auto a = ...;
 switch(a) {
   case 1: a += 1;
   case 2: a += 2;
   // In other cases, leave "a" as is
 }
Just put default: break instead of that comment, it's shorter that way:P
I think the comment was meant as an explanation to you, not something that would actually be in the code :) -Steve
Feb 20 2014
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 19 Feb 2014 04:42:19 -0500, Daniel Murphy  
<yebbliesnospam gmail.com> wrote:

 "Steven Schveighoffer"  wrote in message  
 news:op.xbhnw5rbeav7ka stevens-macbook-pro.local...

 It may not be mindless. Most people who want to handle the default case  
 do it. It's usually not so much "I didn't think of handling other  
 values," it's more "I never expect other values to come in, go away  
 annoying compiler error."
So why not put an assert(0) in instead of a break? Tell the compiler that you're assuming there are no other possible values. This is obviously the right thing to do here, and even if you ignore it the compiler _is_ trying to help you.
Putting default: assert(0); changes the meaning of the code. In other words: switch(x) { case 1: ... } is the same as: switch(x) { case 1: ... default: break; } You are keeping the code the same. I don't think people would think of adding the assert(0), I wouldn't. And as Ary pointed out, it may be that you expect the default to occur, but don't want to do anything.
 I think it's the same as saying you have to always have an else clause  
 after an if statement, even if it's "else {}"
If 'if' was primarily used for the same thing that switch is, this would be perfectly reasonable.
'if' and 'switch' are commonly used interchangeably. Frequently one uses 'switch' where they would normally use 'if' to avoid evaluating something multiple times. -Steve
Feb 20 2014
parent "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Steven Schveighoffer"  wrote in message 
news:op.xbk5cxeoeav7ka stevens-macbook-pro.local...

 Putting default: assert(0); changes the meaning of the code. In other 
 words:

 switch(x)
 {
     case 1: ...
 }

 is the same as:

 switch(x)
 {
     case 1: ...
     default: break;
 }

 You are keeping the code the same. I don't think people would think of 
 adding the assert(0), I wouldn't.
Are we talking about writing new code or porting C/C++ code? If the latter, I agree there is a high chance of just copy-pasting in a default: break; everywhere the error pops up.
 And as Ary pointed out, it may be that you expect the default to occur, 
 but don't want to do anything.
Sure, and you know this when you write the code and choose the correct one.
 'if' and 'switch' are commonly used interchangeably. Frequently one uses 
 'switch' where they would normally use 'if' to avoid evaluating something 
 multiple times.
I'm not sure this is true, at least not in my code.
Feb 20 2014
prev sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
On 2/18/14, 2:26 PM, Daniel Murphy wrote:
 "Steven Schveighoffer"  wrote in message
 news:op.xbhkirppeav7ka stevens-macbook-pro.local...

 My point though, is that the change to require default gains you
 nothing except annoyed programmers. Why put it in?
It only gains you nothing if you respond to the error by mindlessly putting a default: break; in. The compiler is trying to help you by getting you to take an extra second and explicitly state what you what. If you are automatically silencing the error without thinking about the semantics you want, you absolutely should be irritated, but the compiler is not the one doing something stupid here. The awful part of checked exceptions comes from two place IMO - the ide gives you a very very easy way to do thing wrong thing (two clicks IIRC), and doing the right thing is often very very difficult (change exception lists on every calling functions). Here the right and wrong choice are about equal difficulty - trivial. I think complaints about typing those extra couple dozen keystrokes are on the same level as "why do I have to put these ';'s everywhere" and "'immutable' has too many letters".
The compiler should force you to write an "else" for every if then: if (a == 2) { // do something } Error: missing else if (a == 2) { // do something } else { nothing; } Or maybe: if (a == 2) { // do something } else { assert(0); } I think the main complaint is that if you refactor a chain of if/else-if into a switch statement, then you have to add a "default: break;" for sure, which is just redundant.
Feb 18 2014
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/18/2014 8:38 AM, Steven Schveighoffer wrote:
 My point though, is that the change to require default gains you nothing except
 annoyed programmers. Why put it in?
This was fiercely debated at length and settled here years ago. It isn't going to change.
 The comparison I AM making is that we are implementation a requirement that
will
 not achieve the behavior goal it sets out to achieve.
It's been this way for years now, if there was emergent bad behavior, it would be obvious by now. But I haven't heard any reports of such.
Feb 18 2014
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 18 Feb 2014 17:37:43 -0500, Walter Bright  
<newshound2 digitalmars.com> wrote:

 On 2/18/2014 8:38 AM, Steven Schveighoffer wrote:
 My point though, is that the change to require default gains you  
 nothing except
 annoyed programmers. Why put it in?
This was fiercely debated at length and settled here years ago. It isn't going to change.
I thought it was a more recent change than years ago. When was the change made? Looking it up, looks like 2011. That was a long time ago, longer than I thought.
 The comparison I AM making is that we are implementation a requirement  
 that will
 not achieve the behavior goal it sets out to achieve.
It's been this way for years now, if there was emergent bad behavior, it would be obvious by now. But I haven't heard any reports of such.
Emergent bad behavior is not the result I would expect. Most likely, leaving out the default case was not an error. If this fixed a "bug" that didn't exist, the fact that bad behavior didn't result isn't really informative. What I really would be curious about is if in most D code, you see a lot more default: break; than default: assert(0); In any case, I am not vehemently against requiring a default case, it's not terrible to put in default: break; I just find it somewhat puzzling that it's required. -Steve
Feb 20 2014
next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:

 What I really would be curious about is if in most D code, you 
 see a lot more default: break; than default: assert(0);
I have written tons of D2 code in the last years, and such "default: assert(0);" is quite uncommon in my code. Probably less than 8-10 usages in the whole code base. Bye, bearophile
Feb 20 2014
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 20 Feb 2014 10:08:48 -0500, bearophile <bearophileHUGS lycos.com>  
wrote:

 Steven Schveighoffer:

 What I really would be curious about is if in most D code, you see a  
 lot more default: break; than default: assert(0);
I have written tons of D2 code in the last years, and such "default: assert(0);" is quite uncommon in my code. Probably less than 8-10 usages in the whole code base.
But what about default: break;? -Steve
Feb 20 2014
prev sibling parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Steven Schveighoffer"  wrote in message 
news:op.xbk44onleav7ka stevens-macbook-pro.local...

 What I really would be curious about is if in most D code, you see a lot 
 more default: break; than default: assert(0);
I just did a quick git-grep on the compiler source (not D, but all switches do have a default thanks to the d port) With 707 "default:"s 68 had a break on either the same or next line, and 249 had an assert(0). On phobos I get 22 assert(0)s vs 10 breaks with 147 defaults With druntime i get 24 assert(0)s + 5 error();s vs 11 breaks with 64 defaults.
Feb 20 2014
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 20 Feb 2014 10:13:27 -0500, Daniel Murphy  
<yebbliesnospam gmail.com> wrote:

 "Steven Schveighoffer"  wrote in message  
 news:op.xbk44onleav7ka stevens-macbook-pro.local...

 What I really would be curious about is if in most D code, you see a  
 lot more default: break; than default: assert(0);
I just did a quick git-grep on the compiler source (not D, but all switches do have a default thanks to the d port) With 707 "default:"s 68 had a break on either the same or next line, and 249 had an assert(0). On phobos I get 22 assert(0)s vs 10 breaks with 147 defaults With druntime i get 24 assert(0)s + 5 error();s vs 11 breaks with 64 defaults.
Good data, but I was more thinking of people who use D, not the core language. The core language's developers have different behaviors than standard users. I'm not dismissing this data, but I would like to see more application statistics. Another interesting data point would be whether any of those asserts were inserted after the language deprecated missing defaults, or if they existed beforehand. One thing that is nice is how asserts are supported in D. It makes this a much easier decision. -Steve
Feb 20 2014
parent reply Manu <turkeyman gmail.com> writes:
On 21 February 2014 01:20, Steven Schveighoffer <schveiguy yahoo.com> wrote:

 On Thu, 20 Feb 2014 10:13:27 -0500, Daniel Murphy <
 yebbliesnospam gmail.com> wrote:

  "Steven Schveighoffer"  wrote in message news:op.xbk44onleav7ka 
 stevens-macbook-pro.local...

  What I really would be curious about is if in most D code, you see a lot
 more default: break; than default: assert(0);
I just did a quick git-grep on the compiler source (not D, but all switches do have a default thanks to the d port) With 707 "default:"s 68 had a break on either the same or next line, and 249 had an assert(0). On phobos I get 22 assert(0)s vs 10 breaks with 147 defaults With druntime i get 24 assert(0)s + 5 error();s vs 11 breaks with 64 defaults.
Good data, but I was more thinking of people who use D, not the core language. The core language's developers have different behaviors than standard users. I'm not dismissing this data, but I would like to see more application statistics.
In my little app: 17 default: break; 1 default: assert(0); ... and I just realised it should have been a final switch() anyway... so now there's 0. I haven't just been lazy, the default case just happens to be as if an unhandled else in many cases. Another interesting data point would be whether any of those asserts were
 inserted after the language deprecated missing defaults, or if they existed
 beforehand.

 One thing that is nice is how asserts are supported in D. It makes this a
 much easier decision.

 -Steve
Feb 20 2014
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
On 2/20/14, 1:53 PM, Manu wrote:
 On 21 February 2014 01:20, Steven Schveighoffer <schveiguy yahoo.com
 <mailto:schveiguy yahoo.com>> wrote:

     On Thu, 20 Feb 2014 10:13:27 -0500, Daniel Murphy
     <yebbliesnospam gmail.com <mailto:yebbliesnospam gmail.com>> wrote:

         "Steven Schveighoffer"  wrote in message
         news:op.xbk44onleav7ka __stevens-macbook-pro.local...

             What I really would be curious about is if in most D code,
             you see a lot more default: break; than default: assert(0);


         I just did a quick git-grep on the compiler source (not D, but
         all switches do have a default thanks to the d port)

         With 707 "default:"s 68 had a break on either the same or next
         line, and 249 had an assert(0).

         On phobos I get 22 assert(0)s vs 10 breaks with 147 defaults

         With druntime i get 24 assert(0)s + 5 error();s vs 11 breaks
         with 64 defaults.


     Good data, but I was more thinking of people who use D, not the core
     language. The core language's developers have different behaviors
     than standard users. I'm not dismissing this data, but I would like
     to see more application statistics.


 In my little app:
   17 default: break;
   1 default: assert(0); ... and I just realised it should have been a
 final switch() anyway... so now there's 0.
Did you put those "default: break;" because: 1. The compiler told it to do so. 2. You already know the compiler will tell you, so you put it before that happens. In the cases where it was "1.", did it make you think whether you needed to handle the default case?
Feb 20 2014
parent Manu <turkeyman gmail.com> writes:
On 21 February 2014 03:27, Ary Borenszweig <ary esperanto.org.ar> wrote:

 On 2/20/14, 1:53 PM, Manu wrote:

 On 21 February 2014 01:20, Steven Schveighoffer <schveiguy yahoo.com
 <mailto:schveiguy yahoo.com>> wrote:

     On Thu, 20 Feb 2014 10:13:27 -0500, Daniel Murphy
     <yebbliesnospam gmail.com <mailto:yebbliesnospam gmail.com>> wrote:

         "Steven Schveighoffer"  wrote in message
         news:op.xbk44onleav7ka __stevens-macbook-pro.local...


             What I really would be curious about is if in most D code,
             you see a lot more default: break; than default: assert(0);


         I just did a quick git-grep on the compiler source (not D, but
         all switches do have a default thanks to the d port)

         With 707 "default:"s 68 had a break on either the same or next
         line, and 249 had an assert(0).

         On phobos I get 22 assert(0)s vs 10 breaks with 147 defaults

         With druntime i get 24 assert(0)s + 5 error();s vs 11 breaks
         with 64 defaults.


     Good data, but I was more thinking of people who use D, not the core
     language. The core language's developers have different behaviors
     than standard users. I'm not dismissing this data, but I would like
     to see more application statistics.


 In my little app:
   17 default: break;
   1 default: assert(0); ... and I just realised it should have been a
 final switch() anyway... so now there's 0.
Did you put those "default: break;" because: 1. The compiler told it to do so. 2. You already know the compiler will tell you, so you put it before that happens.
Because the compiler told me. It's not a habit of mine to type it. In the cases where it was "1.", did it make you think whether you needed to
 handle the default case?
Never. If I meant to handle the case, I would have already handled the case. I've never forgotten to handle the case when I was supposed to. In the same way I've never forgotten to write else after an if when I intend to.
Feb 20 2014
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 18 February 2014 09:01, Walter Bright <newshound2 digitalmars.com> wrote:

 On 2/17/2014 2:43 PM, Ary Borenszweig wrote:

 Now suppose bit_flag can get an additional "new_bit_flag" value. How does
 "default" helps me notice that I'm supposed to add it to that switch
 statement?
Because if you account for all the cases, you write: default: assert(0); Now you intentionally tell the user that you intentionally covered all the cases.
I think 'final switch' should do that for you, and by typing final, you've intentionally covered the case. There's no room for mistake then.
Feb 17 2014
parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 6:38 PM, Manu wrote:
 I think 'final switch' should do that for you, and by typing final, you've
 intentionally covered the case. There's no room for mistake then.
That's why a default case is not allowed for a 'final' switch.
Feb 17 2014
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 18 February 2014 08:31, Walter Bright <newshound2 digitalmars.com> wrote:

 On 2/17/2014 5:48 AM, Ary Borenszweig wrote:

 On 2/16/14, 5:03 PM, Walter Bright wrote:

 It originally was not required, but there was a campaign by a lot of D
 users to make it required to deal with the common bug of adding a value
 in one switch statement but forgetting to add it to another
 corresponding one.
Could you show an example of such scenario? I don't get it.
Having a set of bit flags, and adding another bit flag later, and failing to account for that in existing switch statements.
It sounds like that's basically the same as final switch, just without the enum key to communicate the strong concept. If I were to imagine a solution to that problem I would have also applied final switch, but in the case it's dealing with integers and not enums and therefore can't know which values are valid and tell you that you missed one, it should insert an implicit 'default: assert(0);' (since final switch isn't meant to have 'default' cases), this way any case you don't explicitly handle is considered invalid, and you'll catch your mistake immediately. I expect final switch on enum's must do this anyway (at least in debug)? It's possible to receive a value that's not a recognised enum key; what happens in that case? It seems effectively the same to me.
Feb 17 2014
parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 6:35 PM, Manu wrote:
 If I were to imagine a solution to that problem I would have also applied final
 switch, but in the case it's dealing with integers and not enums and therefore
 can't know which values are valid and tell you that you missed one, it should
 insert an implicit 'default: assert(0);' (since final switch isn't meant to
have
 'default' cases), this way any case you don't explicitly handle is considered
 invalid, and you'll catch your mistake immediately. I expect final switch on
 enum's must do this anyway (at least in debug)? It's possible to receive a
value
 that's not a recognised enum key; what happens in that case?
 It seems effectively the same to me.
The 'final' works as you propose. Why not give it a whirl?
Feb 17 2014
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/16/14, 7:42 AM, Manu wrote:
 So D offers great improvements to switch(), but there are a few small
 things I wonder about.
TL;DR of my answer to this: at some point we must get used to the notion that minute syntax tweaks are always possible that make us feel we're making progress when instead we're just moving the rubble around.
 1.
 case fall-through is not supported; explicit 'goto case n;' is required.
 With this in mind, 'break' is unnecessary. Why is it required? It could
 be implicit upon reaching the next case label, or a scope could be used
 (with support for omitting the scope for single statements as with if).
 It's really noisy, and annoying to write everywhere.
Implicit fall-through has been specifically eliminated from the language at my behest. I think it is a fine language change. Use "goto case;" to clarify to the maintainer (and incidentally the compiler) you want a fall-through.
 2.
 'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a totally
 different syntax: 'case 1: .. case 3:'

 Why settle on that syntax? The inconsistency looks kinda silly when they
 appear together in code.
 Surely it's possible to find a syntax that works without repeating case
 and ':'?
There's no inconsistency. The case is not identical with "foreach (a .. b)" or arr[a .. b], both of which don't include b in the explored range. The fact that "case b:" is present is very telling "b" will be acted upon.
 It's also weird, because it seems that 'case n: .. case m:' is inclusive
 of m. This may be unexpected.
It's expected.
 I'm not sure it's reasonable to use the '..' syntax in this case for
 that reason. '..' is an [) range, case ranges must be [] so that it
 makes sense when dealing with enum key ranges.
No.
 3.
 Why is 'default' necessary? If I'm not switching on an enumerated type,
 then many values are meaningless. requiring an empty 'default: break;'
 line at the end is annoying and noisy.
Explicit is better than implicit.
 I often find myself tempted to rewrite blocks of successive if() logic
 comparing integers against values/ranges, but it looks silly since the
 scope rules are not explicit, and 'default: break;' always wastes an
 extra line.
Write the line.
 I like to reduce noise in my code, and these switch semantics threaten
 to simplify a lot of code, if not for these strange decisions (purely
 for legacy compliance?).
I think the current switch statement design is a fine design all things considered (compatibility, law of least surprise, usability, readability).
 Let's consider an example:

 Code like this:

 int difficulty = -1;
 if(e.note.note >= 60 && e.note.note < 72)
 difficulty = 0;
 else if(e.note.note >= 72 && e.note.note < 84)
 difficulty = 1;
 else if(e.note.note >= 84 && e.note.note < 96)
 difficulty = 2;
 else if(e.note.note >= 96 && e.note.note < 108)
 difficulty = 3;

 The repetition of e.note.note is annoying, and particular choice of
 comparisons are liable to result in out-by-ones. It's not nice code to read.
For every proposed tweak there will be an example that makes it look great.
 Rewrites like this:

 int difficulty;
 switch(e.note.note)
 {
 case 60: .. case 71:
 difficulty = 0;
 break;
 case 72: .. case 83:
 difficulty = 1;
 break;
 case 84: .. case 95:
 difficulty = 2;
 break;
 case 96: .. case 107:
 difficulty = 3;
 break;
 default:
 difficulty = -1;
 break;
 }

 That's horrid, it's much longer! And there are pointless wasted lines
 everywhere.
 The default case is a total waste, since -1 should just be assigned when
 initialising the variable above.
But you'd be wasting an extra assignment. I recall you're one for efficiency.
 We can compact it a bit like this:

 int difficulty;
 switch(e.note.note)
 {
 case 60: .. case 71:
 difficulty = 0; break;
 case 72: .. case 83:
 difficulty = 1; break;
 case 84: .. case 95:
 difficulty = 2; break;
 case 96: .. case 107:
 difficulty = 3; break;
 default:
 difficulty = -1; break;
 }

 But that's horrible too.
The quality of being horrible is in the eye of the beholder. I find this code entirely reasonable.
 It's not clear what vertical offset the 'break'
 statements shoudl appear at (I hate stacking up multiple statements
 across the same line!).
 The the default case is still a waste.
Just do it.
 Ideally:

 int difficulty = -1;
 switch(e.note.note)
 {
 case 60 .. 72:
 difficulty = 0;
 case 72 .. 84:
 difficulty = 1;
 case 84 .. 96:
 difficulty = 2;
 case 96 .. 108:
 difficulty = 3;
 }
Nope.
 'break's are unnecessary since fallthrough isn't allowed.
Silently change the semantics of C code is not something we entertain doing.
 Proper numeric range could be supported (assuming [) intervals).
 'default' case is unnecessary, and removed.

 The switch and braces results in 3 extra lines, but I still feel this
 level of simplification results in code that is MUCH more readable than
 the sequence of if's I started with. It's super obvious what's going on.

 I have quite many blocks like this.

 A great man once (actually, frequently) said "If it doesn't look right,
 it probably isn't".
The thing is you can apply that to everything, and justify every tweak, because what looks right is subjective. Andrei
Feb 16 2014
next sibling parent reply Manu <turkeyman gmail.com> writes:
On 17 February 2014 16:18, Andrei Alexandrescu <
SeeWebsiteForEmail erdani.org> wrote:

 On 2/16/14, 7:42 AM, Manu wrote:

 So D offers great improvements to switch(), but there are a few small
 things I wonder about.
TL;DR of my answer to this: at some point we must get used to the notion that minute syntax tweaks are always possible that make us feel we're making progress when instead we're just moving the rubble around.
OT: Do you realise how harsh your posts often appear to people? I often find I need to restrain myself when replying to your blunt dismissals of peoples opinions/suggestions. So, you admit that this is a disaster site, but are reluctant to consider what good may come of it? I'm not suggesting to move the rubble around a bit, I'm suggesting a missed opportunity to build something new and useful from the rubble. There are like, 5 control statements in the language; if, for, while, foreach, switch. To trivialise their importance is surprising. switch is basically the same in D as in C, which kinda finds a purpose here and there, but it's built on a rubbish foundation. I can imagine switch (or something like it) being _extremely_ useful to address whole new classes of problems (like match in rust and others) if it were just a little bit less shit. And it is shit, it's barely better than C; it's verbose, nobody knows how to format 'case' statements, it always seems to violate formatting standards whenever it's used (disagrees with all code everywhere else), programmers always seem to disagree on how it should be. In my opinionated opinion, it always seems to look ugly, and unnecessarily verbose. The ugliness of the syntax makes it an unattractive choice for application to many problems where it may have otherwise been applicable, simplifying code, and making the code more readable and understandable at a glance. The switch statement was obviously initially designed to improve clarity; anything that can be done with a switch statement can easily be done with a series of if's (if you love squinting at comparative conditions and constant repetition of the terms being compared), and it probably did improve clarity, in 1970's... But I think we could do much better, and offer an interesting and useful construct that could be used to clarify code in far more locations than it ever sees use today. 1.
 case fall-through is not supported; explicit 'goto case n;' is required.
 With this in mind, 'break' is unnecessary. Why is it required? It could

 be implicit upon reaching the next case label, or a scope could be used
 (with support for omitting the scope for single statements as with if).
 It's really noisy, and annoying to write everywhere.
Implicit fall-through has been specifically eliminated from the language at my behest. I think it is a fine language change. Use "goto case;" to clarify to the maintainer (and incidentally the compiler) you want a fall-through.
It's a patch on a broken design. If switch used proper scoping rules like everything else, nobody would have ever thought that was a reasonable idea. To be clear, perhaps I didn't explain myself clearly; I'm not arguing with the decision about fall-through. I agree that fall-through should be an explicit request. I'm suggesting that in the context of D, where fallthrough is not supported (I offer you congratulations for getting that one in), then 'break;' is entirely pointless, and shouldn't be required. I understand the counter argument to be that "if I paste C code into my D code, there's a potential fail!". And my response is, I think it's lame to gimp D because a language designed 45 years ago had a crappy design. The solution is probably to leave switch as is, and introduce a replacement that's better. 2.
 'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a totally
 different syntax: 'case 1: .. case 3:'

 Why settle on that syntax? The inconsistency looks kinda silly when they
 appear together in code.
 Surely it's possible to find a syntax that works without repeating case
 and ':'?
There's no inconsistency. The case is not identical with "foreach (a .. b)" or arr[a .. b], both of which don't include b in the explored range. The fact that "case b:" is present is very telling "b" will be acted upon.
I never suggested a..b used in a switch should be inclusive. I was just commenting on the choice to use '..' at all. I have said elsewhere that I agree '..' should indeed be exclusive, just like everywhere else. I think the existing syntax is useful for enums, and could remain. Perhaps both should be supported? It's also weird, because it seems that 'case n: .. case m:' is inclusive
 of m. This may be unexpected.
It's expected.
How so? It seems logical in the context of case statements, but '..' does something different in every other instance. If I were new to D, and I saw that, I imagine it would be perfectly reasonable for me to be confused. I'm not sure it's reasonable to use the '..' syntax in this case for
 that reason. '..' is an [) range, case ranges must be [] so that it
 makes sense when dealing with enum key ranges.
No.
No what? 3.
 Why is 'default' necessary? If I'm not switching on an enumerated type,
 then many values are meaningless. requiring an empty 'default: break;'
 line at the end is annoying and noisy.
Explicit is better than implicit.
No. I often find myself tempted to rewrite blocks of successive if() logic
 comparing integers against values/ranges, but it looks silly since the
 scope rules are not explicit, and 'default: break;' always wastes an
 extra line.
Write the line.
No, it looks stupid. If there are few enough cases, I'll use if's instead every time. I lose, and the language loses too. I like to reduce noise in my code, and these switch semantics threaten
 to simplify a lot of code, if not for these strange decisions (purely
 for legacy compliance?).
I think the current switch statement design is a fine design all things considered (compatibility, law of least surprise, usability, readability).
Well, I disagree. It offers some improvement over the catastrophic design that C gave us almost half a century ago, but it doesn't address the basic un-structured nature of the statement, and deliberately retains all the elements that made it rubbish back when (for compatibility). Compatibility and law of least surprise I'll grant you. Usability and readability, I don't think so. Certainly not compared to competition. I think what's clear though, is in order to not violate the first 2 (which I agree there is merit in maintaining), any improvement to switch would probably need to come in the form of a new structure, like 'match' in rust, or something designed from the ground-up to not suck, 'select' maybe. I concede that switch can't be fixed. Perhaps it should be deprecated instead. So I guess on that note, I'll leave it there, but I still think it's worth consideration. The functionality that switch offers is tremendously useful, and it would be so much more useful if it were cleaned up; people would actually use it, and probably in a whole bunch of new applications. I think you'd see a quick erosion of if/else if sequences if switch was more awesome. Let's consider an example:
 Code like this:

 int difficulty = -1;
 if(e.note.note >= 60 && e.note.note < 72)
 difficulty = 0;
 else if(e.note.note >= 72 && e.note.note < 84)
 difficulty = 1;
 else if(e.note.note >= 84 && e.note.note < 96)
 difficulty = 2;
 else if(e.note.note >= 96 && e.note.note < 108)
 difficulty = 3;

 The repetition of e.note.note is annoying, and particular choice of
 comparisons are liable to result in out-by-ones. It's not nice code to
 read.
For every proposed tweak there will be an example that makes it look great.
I see lots and lots of these; opportunities for a structure like an advanced switch to make really nice. Rewrites like this:
 int difficulty;
 switch(e.note.note)
 {
 case 60: .. case 71:
 difficulty = 0;
 break;
 case 72: .. case 83:
 difficulty = 1;
 break;
 case 84: .. case 95:
 difficulty = 2;
 break;
 case 96: .. case 107:
 difficulty = 3;
 break;
 default:
 difficulty = -1;
 break;
 }

 That's horrid, it's much longer! And there are pointless wasted lines
 everywhere.
 The default case is a total waste, since -1 should just be assigned when
 initialising the variable above.
But you'd be wasting an extra assignment. I recall you're one for efficiency.
I've never encountered an optimiser that would fail on something so simple. Even on my Amiga. We can compact it a bit like this:
 int difficulty;
 switch(e.note.note)
 {
 case 60: .. case 71:
 difficulty = 0; break;
 case 72: .. case 83:
 difficulty = 1; break;
 case 84: .. case 95:
 difficulty = 2; break;
 case 96: .. case 107:
 difficulty = 3; break;
 default:
 difficulty = -1; break;
 }

 But that's horrible too.
The quality of being horrible is in the eye of the beholder. I find this code entirely reasonable.
Really? Can you point me to the paragraph in the formatting/style guide that talks about appropriate styling for writing multiple statements on the same line? What happened to DRY? It's not clear what vertical offset the 'break'
 statements shoudl appear at (I hate stacking up multiple statements
 across the same line!).
 The the default case is still a waste.
Just do it.
Nope, I'll use if instead. DRY and all that. I'd rather repeat the term being used for comparison than repeat 'case' and 'break' all over the place. 'if' produces a similar amount of repetition (perhaps less), but results in properly scoped statements, and I know how it should be formatted. Ideally:
 int difficulty = -1;
 switch(e.note.note)
 {
 case 60 .. 72:
 difficulty = 0;
 case 72 .. 84:
 difficulty = 1;
 case 84 .. 96:
 difficulty = 2;
 case 96 .. 108:
 difficulty = 3;
 }
Nope.
Elaborate? 'break's are unnecessary since fallthrough isn't allowed.

 Silently change the semantics of C code is not something we entertain
 doing.
I agree, we'd need to distinguish the fixed version. Proper numeric range could be supported (assuming [) intervals).
 'default' case is unnecessary, and removed.

 The switch and braces results in 3 extra lines, but I still feel this
 level of simplification results in code that is MUCH more readable than
 the sequence of if's I started with. It's super obvious what's going on.

 I have quite many blocks like this.

 A great man once (actually, frequently) said "If it doesn't look right,
 it probably isn't".
The thing is you can apply that to everything, and justify every tweak, because what looks right is subjective.
Are you saying that switch looks pleasing and coherent to you? As compared with all the other flow control statements existing in D, and competitive statements from other languages? Should I start a poll? Perhaps science can tell us if people find it to be regular and pleasant syntactically. Finally, you didn't address the suggestion to allow assignment of the switch condition to a properly scoped variable: switch([x =] expression) ? Some expression results don't have an easily named concept. Performing the expression on a prior line and assigning to a temp often results in some pretty stupid variable names polluting the outer scope. And it's annoying.
Feb 17 2014
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/17/14, 5:33 AM, Manu wrote:
 On 17 February 2014 16:18, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org <mailto:SeeWebsiteForEmail erdani.org>>
 wrote:

     On 2/16/14, 7:42 AM, Manu wrote:

         So D offers great improvements to switch(), but there are a few
         small
         things I wonder about.


     TL;DR of my answer to this: at some point we must get used to the
     notion that minute syntax tweaks are always possible that make us
     feel we're making progress when instead we're just moving the rubble
     around.


 OT: Do you realise how harsh your posts often appear to people? I often
 find I need to restrain myself when replying to your blunt dismissals of
 peoples opinions/suggestions.
You're right. I shouldn't have answered because now the thread is fueling from the answers it's getting. Well actually I did my best in my post to sugarcoat my opinion of the post, which I found a whine on minor issues and a collection of entirely petty proposals. Sorry. Advanced pattern matching and safe downcasting and such - that may be valuable to discuss. But the concerns expressed in this thread build a store on saving on typing 'case' and 'break'.
 So, you admit that this is a disaster site, but are reluctant to
 consider what good may come of it?
I didn't admit that. (And I'm sure this is not an attempt to put words in my mouth.) That wasn't even the charge. I'll actually quote this thread's overture: "So D offers great improvements to switch(), but there are a few small things I wonder about."
 I'm not suggesting to move the rubble around a bit, I'm suggesting a
 missed opportunity to build something new and useful from the rubble.
I believe even if we implement all of these suggestions (change range syntax, "break" no longer required, no need for "default") to perfection, they would have zero consequence on the success of D. I really do believe that.
 There are like, 5 control statements in the language; if, for, while,
 foreach, switch. To trivialise their importance is surprising.
It would be surprising. But nobody is.
 switch is basically the same in D as in C, which kinda finds a purpose
 here and there, but it's built on a rubbish foundation. I can imagine
 switch (or something like it) being _extremely_ useful to address whole
 new classes of problems (like match in rust and others) if it were just
 a little bit less shit.
A match statement would be interesting, but was not asked for. This thread wrings hands over punctuation and formatting. [snip some of the said hand wringing]
     I think the current switch statement design is a fine design all
     things considered (compatibility, law of least surprise, usability,
     readability).


 Well, I disagree. It offers some improvement over the catastrophic
 design that C gave us almost half a century ago, but it doesn't address
 the basic un-structured nature of the statement, and deliberately
 retains all the elements that made it rubbish back when (for compatibility).

 Compatibility and law of least surprise I'll grant you.
 Usability and readability, I don't think so. Certainly not compared to
 competition.

 I think what's clear though, is in order to not violate the first 2
 (which I agree there is merit in maintaining), any improvement to switch
 would probably need to come in the form of a new structure, like 'match'
 in rust, or something designed from the ground-up to not suck, 'select'
 maybe.

 I concede that switch can't be fixed. Perhaps it should be deprecated
 instead.
I don't think we should deprecate switch. And if the to-be-proposed match/select's main features are small syntactic fixes to switch, I don't think they're worth adding.
 So I guess on that note, I'll leave it there, but I still think it's
 worth consideration.
Improving on "switch" is hardly the kind of thing that keeps me awake at night. Finalizing the language is. Threads. Garbage. Null pointers. this(this). Allocators. Dynamic libraries. Better punctuation for "switch" is not even down the list - it's not on the list. And I don't think it should. If that is, anything could be put on the list by the loudest voice asking.
 The functionality that switch offers is tremendously useful, and it
 would be so much more useful if it were cleaned up; people would
 actually use it, and probably in a whole bunch of new applications.
I don't think so. Again, I think a nicer switch syntax would do zero for the success of D. [snip]
 Finally, you didn't address the suggestion to allow assignment of the
 switch condition to a properly scoped variable: switch([x =] expression) ?
 Some expression results don't have an easily named concept. Performing
 the expression on a prior line and assigning to a temp often results in
 some pretty stupid variable names polluting the outer scope. And it's
 annoying.
That bugzilla is a minor improvement that has precedent in the "if" statement, so I'd accept "switch (auto var = expr)" if implemented. Andrei
Feb 17 2014
prev sibling next sibling parent reply "Dejan Lekic" <dejan.lekic gmail.com> writes:
 OT: Do you realise how harsh your posts often appear to people? 
 I often
 find I need to restrain myself when replying to your blunt 
 dismissals of
 peoples opinions/suggestions.

 So, you admit that this is a disaster site, but are reluctant 
 to consider
 what good may come of it?
 I'm not suggesting to move the rubble around a bit, I'm 
 suggesting a missed
 opportunity to build something new and useful from the rubble.

 There are like, 5 control statements in the language; if, for, 
 while,
 foreach, switch. To trivialise their importance is surprising.
 switch is basically the same in D as in C, which kinda finds a 
 purpose here
 and there, but it's built on a rubbish foundation. I can 
 imagine switch (or
 something like it) being _extremely_ useful to address whole 
 new classes of
 problems (like match in rust and others) if it were just a 
 little bit less
 shit.
 And it is shit, it's barely better than C; it's verbose, nobody 
 knows how
 to format 'case' statements, it always seems to violate 
 formatting
 standards whenever it's used (disagrees with all code 
 everywhere else),
 programmers always seem to disagree on how it should be.
 In my opinionated opinion, it always seems to look ugly, and 
 unnecessarily
 verbose.

 The ugliness of the syntax makes it an unattractive choice for 
 application
 to many problems where it may have otherwise been applicable, 
 simplifying
 code, and making the code more readable and understandable at a 
 glance.
 The switch statement was obviously initially designed to 
 improve clarity;
 anything that can be done with a switch statement can easily be 
 done with a
 series of if's (if you love squinting at comparative conditions 
 and
 constant repetition of the terms being compared), and it 
 probably did
 improve clarity, in 1970's...
 But I think we could do much better, and offer an interesting 
 and useful
 construct that could be used to clarify code in far more 
 locations than it
 ever sees use today.
And do you realise that every sentence in your post is matter your your persona taste. It is extremely subjective, and you provide no proof that majority of people who are interested in all this share your opinion, do you? Do not get me wrong, I am not defending Andrei here (he can defend himself better than me). It is just shocking sometimes what kind of non-sense is posted here. Let me dodge back few quotes of your post back at you to think about what I said in the paragraph above: 1) "built on a rubbish foundation" 2) "less shit" (no comment here...!) 3) "nobody knows how to format 'case' statements" (well, if you do not know, there are people who do) 4) "The ugliness of the syntax" (again your own taste) If you scan your own post(s), you will find many examples of what I am saying. This attitude of yours won't get you far.
Feb 17 2014
parent reply Manu <turkeyman gmail.com> writes:
On 18 February 2014 02:48, Dejan Lekic <dejan.lekic gmail.com> wrote:

 [snip]
And do you realise that every sentence in your post is matter your your persona taste. It is extremely subjective, and you provide no proof that majority of people who are interested in all this share your opinion, do you? Do not get me wrong, I am not defending Andrei here (he can defend himself better than me). It is just shocking sometimes what kind of non-sense is posted here.
Yes, they're obviously opinions. I wouldn't use phrasing like that if I wasn't trying to demonstrate my strong opinion on the matter. I only raised some details that are annoying me, I didn't say I had any science to show it was a majority opinion. I'm not sure why that's necessary to make an opinion oriented post. A large part of the reason for posting in the first place is to find if/how many people also feel that way. That's the nature of opinions, I don't know what other peoples are until I talk about it. Let me dodge back few quotes of your post back at you to think about what I
 said in the paragraph above:
 1) "built on a rubbish foundation"
 2) "less shit" (no comment here...!)
 3) "nobody knows how to format 'case' statements" (well, if you do not
 know, there are people who do)
 4) "The ugliness of the syntax" (again your own taste)

 If you scan your own post(s), you will find many examples of what I am
 saying. This attitude of yours won't get you far.
Well, I firmly stand by all those convictions. They also look worse out of context. I don't think switch is particularly good, and firmly believe C's switch statement was (is) a design catastrophe. I've believed that for 20 years. You're welcome to disagree, and tell me why you do. I might even change my opinion if someone can convince me it's awesome the way it is for some reason. I've spent 3 days basically writing switch statements. I haven't enjoyed it, and I feel the experience is a big missed opportunity.
Feb 17 2014
parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 10:03 AM, Manu wrote:
 Well, I firmly stand by all those convictions. They also look worse out of
context.
 I don't think switch is particularly good, and firmly believe C's switch
 statement was (is) a design catastrophe. I've believed that for 20 years.
You're
 welcome to disagree, and tell me why you do. I might even change my opinion if
 someone can convince me it's awesome the way it is for some reason.
 I've spent 3 days basically writing switch statements. I haven't enjoyed it,
and
 I feel the experience is a big missed opportunity.
Having strong convictions is good, but strong reactions to them come with the territory :-)
Feb 17 2014
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 5:33 AM, Manu wrote:
         We can compact it a bit like this:

         int difficulty;
         switch(e.note.note)
         {
         case 60: .. case 71:
         difficulty = 0; break;
         case 72: .. case 83:
         difficulty = 1; break;
         case 84: .. case 95:
         difficulty = 2; break;
         case 96: .. case 107:
         difficulty = 3; break;
         default:
         difficulty = -1; break;
         }

         But that's horrible too.
I tend to format such like this: int difficulty; switch(e.note.note) { case 60: .. case 71: difficulty = 0; break; case 72: .. case 83: difficulty = 1; break; case 84: .. case 95: difficulty = 2; break; case 96: .. case 107: difficulty = 3; break; default: difficulty = -1; break; } By lining things up, it takes on a tabular appearance. People are good at inferring patterns, and such tabular arrangements make it easy to spot squeaky wheels.
 Finally, you didn't address the suggestion to allow assignment of the switch 
 condition to a properly scoped variable: switch([x =] expression) ?
That's probably a good idea.
Feb 17 2014
parent reply Manu <turkeyman gmail.com> writes:
On 18 February 2014 06:00, Walter Bright <newshound2 digitalmars.com> wrote:
 I tend to format such like this:


          int difficulty;
          switch(e.note.note)
          {
            case 60: .. case 71:  difficulty = 0;  break;
            case 72: .. case 83:  difficulty = 1;  break;
            case 84: .. case 95:  difficulty = 2;  break;
            case 96: .. case 107: difficulty = 3;  break;
            default:              difficulty = -1; break;
          }

 By lining things up, it takes on a tabular appearance. People are good at
 inferring patterns, and such tabular arrangements make it easy to spot
 squeaky wheels.
Me too, but you don't feel this is basically a hack? About half of that text is repeated cruft, and there's no precedent for formatting well-structured code like that anywhere else in the language. How long would you say you spend on average fiddling with the tabulation? I am known to spend minutes pressing the space bar, trying to make it line up and look nice. And then invariably, some other case comes along, with a slightly longer identifier name, and you have to work your way up shifting everything around against with a bunch more spaces.. pollutes source control history, etc. And then that case with the REALLY long identifier name comes along, and you end out with so many spaces surrounding all the other cases, that it becomes difficult to associate which line matches which case, so then you think to yourself, "this one long case is ruining my code, maybe I should break the pattern and fall this long one onto it's own line below...", and then you're just wasting time, pushing the spacebar key, trying to work around something that shouldn't have been an issue in the first place. Often enough the break statements end up far off the right hand side of the screen due to that one long statement in the sequence; do you line them all up religiously far to the right? Or do you make an exception for that one long line, allowing it to span beyond the 'break' line, and keep the rest lined up nearer to the code they terminate? Tell me this doesn't happen to you? Surely I'm not the only one that faces this sort of conundrum frequently when I try and use switch? :) I can't see that practise as anything other than a formatting hack to deal with something that was structurally unsound in the first place.
 Finally, you didn't address the suggestion to allow assignment of the
 switch > condition to a properly scoped variable: switch([x =] expression) ?

 That's probably a good idea.
Cool, I'll bugzilla it. At least something useful may comes from my ranting :P
Feb 17 2014
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 2/17/2014 6:54 PM, Manu wrote:
 Me too, but you don't feel this is basically a hack?
I see nothing hackish about it. After all, there's a reason D does not enforce whitespace.
 About half of that text is repeated cruft, and there's no precedent for
 formatting well-structured code like that anywhere else in the language.
 How long would you say you spend on average fiddling with the tabulation?
 I am known to spend minutes pressing the space bar, trying to make it line up
 and look nice. And then invariably, some other case comes along, with a
slightly
 longer identifier name, and you have to work your way up shifting everything
 around against with a bunch more spaces.. pollutes source control history, etc.
 And then that case with the REALLY long identifier name comes along, and you
end
 out with so many spaces surrounding all the other cases, that it becomes
 difficult to associate which line matches which case, so then you think to
 yourself, "this one long case is ruining my code, maybe I should break the
 pattern and fall this long one onto it's own line below...", and then you're
 just wasting time, pushing the spacebar key, trying to work around something
 that shouldn't have been an issue in the first place.
 Often enough the break statements end up far off the right hand side of the
 screen due to that one long statement in the sequence; do you line them all up
 religiously far to the right? Or do you make an exception for that one long
 line, allowing it to span beyond the 'break' line, and keep the rest lined up
 nearer to the code they terminate?
 Tell me this doesn't happen to you? Surely I'm not the only one that faces this
 sort of conundrum frequently when I try and use switch? :)
Shirley you're joking. If that's the hardest problem you face programming in D, I am well satisfied that D is a great design!
Feb 17 2014
next sibling parent "Brian Schott" <briancschott gmail.com> writes:
On Tuesday, 18 February 2014 at 04:02:35 UTC, Walter Bright wrote:
 Shirley you're joking. If that's the hardest problem you face 
 programming in D, I am well satisfied that D is a great design!
http://www.youtube.com/watch?feature=player_detailpage&v=KM2K7sV-K74#t=5
Feb 17 2014
prev sibling parent Manu <turkeyman gmail.com> writes:
On 18 February 2014 14:02, Walter Bright <newshound2 digitalmars.com> wrote:
 Shirley you're joking. If that's the hardest problem you face programming
 in D, I am well satisfied that D is a great design!
I didn't say that. But I do think it's more significant than it appears. If people deliberately avoid a fundamental structure because it's awkward and in many cases it doesn't offer a clarification of their code in practise, when it easily could/should have, I think that's a significant loss. Note; that's a commentary on one criticism relating to one particular pasted block of code wrt switch as it exists right now. It's not a summary of all my thoughts presented in this thread, just a single facet of my post.
Feb 17 2014
prev sibling next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/17/2014 9:54 PM, Manu wrote:
 On 18 February 2014 06:00, Walter Bright <newshound2 digitalmars.com> wrote:
 I tend to format such like this:


           int difficulty;
           switch(e.note.note)
           {
             case 60: .. case 71:  difficulty = 0;  break;
             case 72: .. case 83:  difficulty = 1;  break;
             case 84: .. case 95:  difficulty = 2;  break;
             case 96: .. case 107: difficulty = 3;  break;
             default:              difficulty = -1; break;
           }

 By lining things up, it takes on a tabular appearance. People are good at
 inferring patterns, and such tabular arrangements make it easy to spot
 squeaky wheels.
Me too, but you don't feel this is basically a hack? About half of that text is repeated cruft, and there's no precedent for formatting well-structured code like that anywhere else in the language. How long would you say you spend on average fiddling with the tabulation?
I use the formatting posted above, and it takes me very, very little time.
 I am known to spend minutes pressing the space bar, trying to make it line
 up and look nice. And then invariably, some other case comes along, with a
 slightly longer identifier name, and you have to work your way up shifting
 everything around against with a bunch more spaces.. pollutes source
 control history, etc.
Sounds like you need elastic tabstops: http://nickgravgaard.com/elastictabstops/ I don't use them personally since PN2 doesn't support them yet, but they're a brilliant idea and they do solve that re-aligning issue. Word processors figured out how tabstops are supposed to work ages ago. Elastic tabstops really just amount to bringing code editors' ancient tab-handling up-to-date with the 90's.
 And then that case with the REALLY long identifier name comes along, and
 you end out with so many spaces surrounding all the other cases, that it
 becomes difficult to associate which line matches which case, so then you
 think to yourself, "this one long case is ruining my code, maybe I should
 break the pattern and fall this long one onto it's own line below...", and
 then you're just wasting time, pushing the spacebar key, trying to work
 around something that shouldn't have been an issue in the first place.
 Often enough the break statements end up far off the right hand side of the
 screen due to that one long statement in the sequence; do you line them all
 up religiously far to the right? Or do you make an exception for that one
 long line, allowing it to span beyond the 'break' line, and keep the rest
 lined up nearer to the code they terminate?
 Tell me this doesn't happen to you? Surely I'm not the only one that faces
 this sort of conundrum frequently when I try and use switch? :)
Erm, between all of that, plus your strong objection to "default: break;", I really do get the impression you're just simply being very OCD about this stuff. I don't mean that as an insult, I just think it's all a bit "Adrian Monk of source code", if you're familiar with the reference.
Feb 17 2014
parent "Casper =?UTF-8?B?RsOmcmdlbWFuZCI=?= <shorttail hotmail.com> writes:
On Tuesday, 18 February 2014 at 06:00:25 UTC, Nick Sabalausky 
wrote:
 Erm, between all of that, plus your strong objection to 
 "default: break;", I really do get the impression you're just 
 simply being very OCD about this stuff. I don't mean that as an 
 insult, I just think it's all a bit "Adrian Monk of source 
 code", if you're familiar with the reference.
I have code formatting OCD. Switch case is an abomination. It looks nice with spaces and all, but anything that can't be identified by an auto formatter should die in a fire. Also, I use arbitrary empty structs in my multi threading code as tokens. The type pattern matching of the mailbox looks nice. Unlike the switch case statements. Tl;dr: Manual indentation, ew. Identically indented } on multiple lines, ew.
Feb 18 2014
prev sibling parent "QAston" <qaston gmail.com> writes:
On Tuesday, 18 February 2014 at 02:55:10 UTC, Manu wrote:
 Me too, but you don't feel this is basically a hack?
 About half of that text is repeated cruft, and there's no 
 precedent for
 formatting well-structured code like that anywhere else in the 
 language.
There are other ways in D to deal with repetition. For people coming from C/C++ some of these tricks are unnatural because programmers writing in those prefer to just write everything "inline" for performance reasons. So, possible sollutions: - go make a function: getDifficulty(int note) { switch(note) { case 60: .. case 71: return 0; case 72: .. case 83: return 1; case 84: .. case 95: return 2; case 96: .. case 107: return 3; default: return -1; } } and then difficulty = getDifficulty(e.note.note); - you can make this function local if it's not needed anywhere, local functions are awesome - they reduce a lot of code duplication. Any code that repeats inside a local scope can be put inside a local function - you can also make e.note a real type with a method instead of inlining the logic in place and call e.note.getDifficulty or whatever If your code is full of switch-cases you should probably use polymorphism instead (virtual dispatch, omg).
Feb 19 2014
prev sibling next sibling parent Kenji Hara <k.hara.pg gmail.com> writes:
2014-02-17 22:33 GMT+09:00 Manu <turkeyman gmail.com>:

 On 17 February 2014 16:18, Andrei Alexandrescu <
 SeeWebsiteForEmail erdani.org> wrote:

 On 2/16/14, 7:42 AM, Manu wrote:

 So D offers great improvements to switch(), but there are a few small
 things I wonder about.
TL;DR of my answer to this: at some point we must get used to the notion that minute syntax tweaks are always possible that make us feel we're making progress when instead we're just moving the rubble around.
OT: Do you realise how harsh your posts often appear to people? I often find I need to restrain myself when replying to your blunt dismissals of peoples opinions/suggestions. So, you admit that this is a disaster site, but are reluctant to consider what good may come of it? I'm not suggesting to move the rubble around a bit, I'm suggesting a missed opportunity to build something new and useful from the rubble.
I completely agree with Andrei. We should continue to keep that D is the successor of the most used system languages - C and C++. it's a *huge* advantage against other modern languages. Kenji Hara
Feb 17 2014
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 18 February 2014 01:02, Kenji Hara <k.hara.pg gmail.com> wrote:

 2014-02-17 22:33 GMT+09:00 Manu <turkeyman gmail.com>:

 On 17 February 2014 16:18, Andrei Alexandrescu <
 SeeWebsiteForEmail erdani.org> wrote:

 On 2/16/14, 7:42 AM, Manu wrote:

 So D offers great improvements to switch(), but there are a few small
 things I wonder about.
TL;DR of my answer to this: at some point we must get used to the notion that minute syntax tweaks are always possible that make us feel we're making progress when instead we're just moving the rubble around.
OT: Do you realise how harsh your posts often appear to people? I often find I need to restrain myself when replying to your blunt dismissals of peoples opinions/suggestions. So, you admit that this is a disaster site, but are reluctant to consider what good may come of it? I'm not suggesting to move the rubble around a bit, I'm suggesting a missed opportunity to build something new and useful from the rubble.
I completely agree with Andrei. We should continue to keep that D is the successor of the most used system languages - C and C++. it's a *huge* advantage against other modern languages.
I agree, to an extent. That's why I say if it is to be improved, I guess it needs a new name. foreach eliminated almost all instances of for. I don't think anyone's upset about that.
Feb 17 2014
parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Manu" <turkeyman gmail.com> wrote in message 
news:mailman.156.1392649746.6445.digitalmars-d puremagic.com...

 I agree, to an extent. That's why I say if it is to be improved, I guess 
 it needs a new
 name.
 foreach eliminated almost all instances of for. I don't think anyone's 
 upset about
 that.
Exactly, foreach is a new, better language construct, but we didn't butcher for. I would love to see a nice solid proposal for 'match'.
Feb 17 2014
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/17/14, 7:42 AM, Daniel Murphy wrote:
 "Manu" <turkeyman gmail.com> wrote in message
 news:mailman.156.1392649746.6445.digitalmars-d puremagic.com...

 I agree, to an extent. That's why I say if it is to be improved, I
 guess it needs a new
 name.
 foreach eliminated almost all instances of for. I don't think anyone's
 upset about
 that.
Exactly, foreach is a new, better language construct, but we didn't butcher for. I would love to see a nice solid proposal for 'match'.
A "match" statement that figures type patterns and introduce names and all that - that might be more interesting (though being far from a game changer). But that's not quite what has been proposed in this thread. Andrei
Feb 17 2014
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 2/17/14, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 A "match" statement that figures type patterns and introduce names and
 all that - that might be more interesting (though being far from a game
 changer). But that's not quite what has been proposed in this thread.
There are some interesting related Phobos pulls: Add functional style regex pattern-matching: https://github.com/D-Programming-Language/phobos/pull/1392 Add functional pattern matching for object references: https://github.com/D-Programming-Language/phobos/pull/1266 Add a functional switch function: https://github.com/D-Programming-Language/phobos/pull/1259
Feb 17 2014
prev sibling next sibling parent "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Andrei Alexandrescu"  wrote in message news:ldtbcb$mel$1 digitalmars.com...

 A "match" statement that figures type patterns and introduce names and all 
 that - that might be more interesting (though being far from a game 
 changer).
I doubt one can really know if it's a game changer until it has been implemented and experimented with. I'm not really in a position to design one for D, but I have had some positive experience with erlang's 'case' expressions. The real question is if we can make it clean and powerful enough to outdo a library solution.
 But that's not quite what has been proposed in this thread.
Yeah, I was trying to derail this thread in a more useful direction.
Feb 17 2014
prev sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/17/2014 10:54 AM, Andrei Alexandrescu wrote:
 On 2/17/14, 7:42 AM, Daniel Murphy wrote:
 Exactly, foreach is a new, better language construct, but we didn't
 butcher for.  I would love to see a nice solid proposal for 'match'.
A "match" statement that figures type patterns and introduce names and all that - that might be more interesting (though being far from a game changer). But that's not quite what has been proposed in this thread.
Although I've said it before, I'd love to see Nemerle's match in D: https://github.com/rsdn/nemerle/wiki/Quick-guide#wiki-Pattern_Matching Especially together with its great algebraic types (Phobos's Algebraic still needs some beefing up, unfortunately).
Feb 17 2014
prev sibling next sibling parent "Kapps" <opantm2+spam gmail.com> writes:
On Sunday, 16 February 2014 at 15:43:31 UTC, Manu wrote:
 int difficulty;
 switch(e.note.note)
 {
 case 60: .. case 71:
 difficulty = 0;
 break;
 case 72: .. case 83:
 difficulty = 1;
 break;
 case 84: .. case 95:
 difficulty = 2;
 break;
 case 96: .. case 107:
 difficulty = 3;
 break;
 default:
 difficulty = -1;
 break;
 }
I do agree that the breaks can get rather messy and I don't think that the current syntax is perfect. However I also find that in most situations where a switch statement is useful, it's not a bad idea to have it as a separate method anyways. In this case: int getDifficulty(int note) { switch(note) { case 60: .. case 71: return 0; case 72: .. case 83: return 1; case 84: .. case 95: return 2; case 96: .. case 107: return 3; default: return -1; } } Since it's using a return there's no break required and it's arguably a cleaner separation.
Feb 17 2014
prev sibling next sibling parent "Damian Day" <damianroyday gmail.com> writes:
On Sunday, 16 February 2014 at 15:43:31 UTC, Manu wrote:
 So D offers great improvements to switch(), but there are a few 
 small
 things I wonder about.

 1.
 case fall-through is not supported; explicit 'goto case n;' is 
 required.
 With this in mind, 'break' is unnecessary. Why is it required? 
 It could be
 implicit upon reaching the next case label, or a scope could be 
 used (with
 support for omitting the scope for single statements as with 
 if).
 It's really noisy, and annoying to write everywhere.

 2.
 'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a 
 totally different
 syntax: 'case 1: .. case 3:'

 Why settle on that syntax? The inconsistency looks kinda silly 
 when they
 appear together in code.
 Surely it's possible to find a syntax that works without 
 repeating case and
 ':'?

 It's also weird, because it seems that 'case n: .. case m:' is 
 inclusive of
 m. This may be unexpected.
 I'm not sure it's reasonable to use the '..' syntax in this 
 case for that
 reason. '..' is an [) range, case ranges must be [] so that it 
 makes sense
 when dealing with enum key ranges.

 3.
 Why is 'default' necessary? If I'm not switching on an 
 enumerated type,
 then many values are meaningless. requiring an empty 'default: 
 break;' line
 at the end is annoying and noisy.

 I often find myself tempted to rewrite blocks of successive 
 if() logic
 comparing integers against values/ranges, but it looks silly 
 since the
 scope rules are not explicit, and 'default: break;' always 
 wastes an extra
 line.
 I like to reduce noise in my code, and these switch semantics 
 threaten to
 simplify a lot of code, if not for these strange decisions 
 (purely for
 legacy compliance?).


 Let's consider an example:

 Code like this:

 int difficulty = -1;
 if(e.note.note >= 60 && e.note.note < 72)
 difficulty = 0;
 else if(e.note.note >= 72 && e.note.note < 84)
 difficulty = 1;
 else if(e.note.note >= 84 && e.note.note < 96)
 difficulty = 2;
 else if(e.note.note >= 96 && e.note.note < 108)
 difficulty = 3;

 The repetition of e.note.note is annoying, and particular 
 choice of
 comparisons are liable to result in out-by-ones. It's not nice 
 code to read.


 Rewrites like this:

 int difficulty;
 switch(e.note.note)
 {
 case 60: .. case 71:
 difficulty = 0;
 break;
 case 72: .. case 83:
 difficulty = 1;
 break;
 case 84: .. case 95:
 difficulty = 2;
 break;
 case 96: .. case 107:
 difficulty = 3;
 break;
 default:
 difficulty = -1;
 break;
 }

 That's horrid, it's much longer! And there are pointless wasted 
 lines
 everywhere.
 The default case is a total waste, since -1 should just be 
 assigned when
 initialising the variable above.


 We can compact it a bit like this:

 int difficulty;
 switch(e.note.note)
 {
 case 60: .. case 71:
 difficulty = 0; break;
 case 72: .. case 83:
 difficulty = 1; break;
 case 84: .. case 95:
 difficulty = 2; break;
 case 96: .. case 107:
 difficulty = 3; break;
 default:
 difficulty = -1; break;
 }

 But that's horrible too. It's not clear what vertical offset 
 the 'break'
 statements shoudl appear at (I hate stacking up multiple 
 statements across
 the same line!).
 The the default case is still a waste.


 Ideally:

 int difficulty = -1;
 switch(e.note.note)
 {
 case 60 .. 72:
 difficulty = 0;
 case 72 .. 84:
 difficulty = 1;
 case 84 .. 96:
 difficulty = 2;
 case 96 .. 108:
 difficulty = 3;
 }

 'break's are unnecessary since fallthrough isn't allowed.
 Proper numeric range could be supported (assuming [) intervals).
 'default' case is unnecessary, and removed.

 The switch and braces results in 3 extra lines, but I still 
 feel this level
 of simplification results in code that is MUCH more readable 
 than the
 sequence of if's I started with. It's super obvious what's 
 going on.

 I have quite many blocks like this.

 A great man once (actually, frequently) said "If it doesn't 
 look right, it
 probably isn't".
Having also wrote a lot of switch statements in D I do mostly agree on these points. I think this example 'case 1: .. case 3:' is obvious. but I like 'case 1..3, 5, 6, 8, 9..10:' syntax better (Pascal) see how much typing I save. Break inside a switch should be rid of (Again Pascal) it's bloat. Having a default does get a bit annoying I see a lot of these.. That being said it's nice having that extra safety.
Feb 17 2014
prev sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 16 February 2014 at 15:43:31 UTC, Manu wrote:
 1.
 case fall-through is not supported;
goto case; automagically goes to the next case.
 2.
 'case 1, 3, 7, 8:' is awesome! ...but ranged cases have a 
 totally different
 syntax: 'case 1: .. case 3:'

 Why settle on that syntax? The inconsistency looks kinda silly 
 when they
 appear together in code.
When implementing this on SDC. The .. syntax seems appropriate to me, the current one is clearly insane.
 3.
 Why is 'default' necessary? If I'm not switching on an 
 enumerated type,
 then many values are meaningless. requiring an empty 'default: 
 break;' line
 at the end is annoying and noisy.
Sounds like an improvement over conventional switches to me.
Feb 18 2014