www.digitalmars.com         C & C++   DMDScript  

D - switch statement improvement?

reply Ian Woollard <Ian_member pathlink.com> writes:
Just had a quick look at the switch statement.

I noticed the syntax is:

switch (foobar)
{
case 0:
dostuff();
// I've forgotten to break; all is lost.
case 1:
domorestuffIdidntreallywantto();
break;
default:
}

This isn't such a good idea. The default behaviour is really unsafe. I would
suggest a 'nobreak' statement for cases where you really do want the code to
fall through; otherwise  the compiler probably should fail to compile (possibly
switcheable off with a compiler option.)

e.g.

switch (foobar)
{
case 0:
dostuff();
nobreak; // we fall through, but that's fine.
case 1:
case 2: // note no nobreak required (although I wouldn't cry if it was)
domorestuff();
break;
default:
}

Anyway, just an idea; probably a very good one IMNHO though.

You might also consider the same idea in empty if and for statements.

-Ian
Sep 15 2003
next sibling parent reply "Charles Sanders" <sanders-consulting comcast.net> writes:
I like the idea of a nobreak statement ( this has been a popular topic) , I
think the reason for keeping this is backward compatibility ( on a user
level ).

I always hated this behavior, and could never understand why this was the
default.  Can we change it ?  Taking a vote might be helpful to Walter.  I
vote we fix this beast once and for all!

Charles

"Ian Woollard" <Ian_member pathlink.com> wrote in message
news:bk5nri$14cm$1 digitaldaemon.com...
 Just had a quick look at the switch statement.

 I noticed the syntax is:

 switch (foobar)
 {
 case 0:
 dostuff();
 // I've forgotten to break; all is lost.
 case 1:
 domorestuffIdidntreallywantto();
 break;
 default:
 }

 This isn't such a good idea. The default behaviour is really unsafe. I

 suggest a 'nobreak' statement for cases where you really do want the code

 fall through; otherwise  the compiler probably should fail to compile

 switcheable off with a compiler option.)

 e.g.

 switch (foobar)
 {
 case 0:
 dostuff();
 nobreak; // we fall through, but that's fine.
 case 1:
 case 2: // note no nobreak required (although I wouldn't cry if it was)
 domorestuff();
 break;
 default:
 }

 Anyway, just an idea; probably a very good one IMNHO though.

 You might also consider the same idea in empty if and for statements.

 -Ian

Sep 15 2003
next sibling parent reply Ian Woollard <Ian_member pathlink.com> writes:
In article <bk5o47$15o3$1 digitaldaemon.com>, Charles Sanders says...
I like the idea of a nobreak statement ( this has been a popular topic) , I
think the reason for keeping this is backward compatibility ( on a user
level ).

Provided the nobreak statement is optional (just a nasty compiler warning if left out), or mandatory but the compiler has a flag to make it ignore it's omission (but still warn- my colleagues ARE out to get me :-) ), then backward compatibility is, in practice, a non issue.
Charles

"Ian Woollard" <Ian_member pathlink.com> wrote in message
news:bk5nri$14cm$1 digitaldaemon.com...
 Just had a quick look at the switch statement.

 I noticed the syntax is:

 switch (foobar)
 {
 case 0:
 dostuff();
 // I've forgotten to break; all is lost.
 case 1:
 domorestuffIdidntreallywantto();
 break;
 default:
 }

 This isn't such a good idea. The default behaviour is really unsafe. I

 suggest a 'nobreak' statement for cases where you really do want the code

 fall through; otherwise  the compiler probably should fail to compile

 switcheable off with a compiler option.)

 e.g.

 switch (foobar)
 {
 case 0:
 dostuff();
 nobreak; // we fall through, but that's fine.
 case 1:
 case 2: // note no nobreak required (although I wouldn't cry if it was)
 domorestuff();
 break;
 default:
 }

 Anyway, just an idea; probably a very good one IMNHO though.

 You might also consider the same idea in empty if and for statements.

 -Ian


Sep 15 2003
next sibling parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
I like the idea of a nobreak statement ( this has been a popular topic) ,


think the reason for keeping this is backward compatibility ( on a user
level ).

Provided the nobreak statement is optional (just a nasty compiler warning

 left out), or mandatory but the compiler has a flag to make it ignore it's
 omission (but still warn- my colleagues ARE out to get me :-) ), then

 compatibility is, in practice, a non issue.

I think that nobreak is a good keyword and should be required if no it would otherwise fall-through. That is, we ne it if there are no break, return, throw, continue or goto statement (or similar). I think that compatibility is not a good reason in this case to make it facultative. In my opinion even a good programmer might forget one or two break in a year and spend a few minutes to find a bug that could be easily prevented by the compiler by requiring explicit exit... IMO, the time saved in debugging for common programmers worth well the few added keystroke... and if break is optional, we would be safer than C++ with less code to write in typical application (since more than 90% of case needs a break). For compatibility with C++, with should allows to still uses break (easy conversion from C++ to D) and define nobreak as an empty statement in C++ (for easier conversion in the other direction). #define nobreak (void)0 /* in C++ */ That way, by using both break and nobreak the code is portable between C++ and D. Also, I would suggest to make these kind of change soon before a lot of code depend on it... I wote that fall-through must be explicitly asked (nobreak or something else)... For weither break is required or optional, I would prefer optional but I can live with if required... And I like nobreak as it is explicit, short and documentative... but labelled jump is acceptable... and a bit safer if the order is modified!
Sep 15 2003
parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
I think that both break and nobreak (I'd prefer "fallthrough") were
required. That's the safest (albeit the most verbose) way to go

"Philippe Mori" <philippe_mori hotmail.com> wrote in message
news:bk5uqe$1tnn$1 digitaldaemon.com...
I like the idea of a nobreak statement ( this has been a popular topic)



 I
think the reason for keeping this is backward compatibility ( on a user
level ).

Provided the nobreak statement is optional (just a nasty compiler


 if
 left out), or mandatory but the compiler has a flag to make it ignore


 omission (but still warn- my colleagues ARE out to get me :-) ), then

 compatibility is, in practice, a non issue.

I think that nobreak is a good keyword and should be required if no it

 otherwise fall-through. That is, we ne it if there are no break, return,
 throw,
 continue or goto statement (or similar).

 I think that compatibility is not a good reason in this case to make it
 facultative. In my opinion even a good programmer might forget one
 or two break in a year and spend a few minutes to find a bug that
 could be easily prevented by the compiler by requiring explicit
 exit... IMO, the time saved in debugging for common programmers
 worth well the few added keystroke... and if break is optional, we
 would be safer than C++ with less code to write in typical application
 (since more than 90% of case needs a break).

  For compatibility with C++, with should allows to still uses break
 (easy conversion from C++ to D) and define nobreak as an empty
 statement in C++ (for easier conversion in the other direction).

 #define nobreak (void)0    /* in C++ */

 That way, by using both break and nobreak the code is portable
 between C++ and D.

 Also, I would suggest to make these kind of change soon before
 a lot of code depend on it...

 I wote that fall-through must be explicitly asked (nobreak or
 something else)... For weither break is required or optional,
 I would prefer optional but I can live with if required...

 And I like nobreak as it is explicit, short and documentative...
 but labelled jump is acceptable... and a bit safer if the
 order is modified!

Sep 15 2003
parent reply Helmut Leitner <helmut.leitner chello.at> writes:
Matthew Wilson wrote:
 
 I think that both break and nobreak (I'd prefer "fallthrough") were
 required. That's the safest (albeit the most verbose) way to go
 

Exactly. I would also vote to make "break or nobreak" mandatory. So you can convert code from all C family languages without any risk. -- Helmut Leitner leitner hls.via.at Graz, Austria www.hls-software.com
Sep 15 2003
parent "Matthew Wilson" <matthew stlsoft.org> writes:
 I think that both break and nobreak (I'd prefer "fallthrough") were
 required. That's the safest (albeit the most verbose) way to go

Exactly. I would also vote to make "break or nobreak" mandatory. So you can convert code from all C family languages without any risk.

Yes, indeed. That was what I meant (and why I meant it :) ).
Sep 15 2003
prev sibling parent reply John Boucher <John_member pathlink.com> writes:
Hmmm, a compiler switch to say whether or not it is required... kinda starting
to border on being a pragma, isn't it?

In article <bk5pk9$1a54$1 digitaldaemon.com>, Ian Woollard says...
In article <bk5o47$15o3$1 digitaldaemon.com>, Charles Sanders says...
I like the idea of a nobreak statement ( this has been a popular topic) , I
think the reason for keeping this is backward compatibility ( on a user
level ).

Provided the nobreak statement is optional (just a nasty compiler warning if left out), or mandatory but the compiler has a flag to make it ignore it's omission (but still warn- my colleagues ARE out to get me :-) ), then backward compatibility is, in practice, a non issue.
Charles


Sep 15 2003
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
I recall that there are good reasons for not having compile-time options
that change the semantics of the code. If my memory is not failing me,
Walter said somewhere that he has a rather strong opinion about this.

Generally I think the same, but I'd allow the following border case:

- Default behavior of switch statement is to break.
- Keyword "fallthrough" is required to, well, fall through.
- A compiler-switch "-require-explicit-break-or-fallthrough" that will
  require explicit "break;" or "fallthrough;". The purpose of this
  switch is to make porting C apps easier.

That way you could write native D apps without writing the tedious break
statement. After all, as Philippe said, the behavior in 9 out of
10 cases is to break. The superfluous breaks just clutter up the code.

If you'd need to port C apps, just use the option
-require-explicit-break-or-fallthrough and insert "fallthrough" where
there is no break. After the code compiles, you can remove the option
and works correctly. There will be a couple of redundant break
statements but they won't hurt anyone.

-Antti

In article <bk615t$22md$1 digitaldaemon.com>, John Boucher wrote:
 Hmmm, a compiler switch to say whether or not it is required... kinda starting
 to border on being a pragma, isn't it?
 
 In article <bk5pk9$1a54$1 digitaldaemon.com>, Ian Woollard says...
In article <bk5o47$15o3$1 digitaldaemon.com>, Charles Sanders says...
I like the idea of a nobreak statement ( this has been a popular topic) , I
think the reason for keeping this is backward compatibility ( on a user
level ).

Provided the nobreak statement is optional (just a nasty compiler warning if left out), or mandatory but the compiler has a flag to make it ignore it's omission (but still warn- my colleagues ARE out to get me :-) ), then backward compatibility is, in practice, a non issue.
Charles



Sep 16 2003
parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 I recall that there are good reasons for not having compile-time options
 that change the semantics of the code. If my memory is not failing me,
 Walter said somewhere that he has a rather strong opinion about this.

 Generally I think the same, but I'd allow the following border case:

 - Default behavior of switch statement is to break.
 - Keyword "fallthrough" is required to, well, fall through.
 - A compiler-switch "-require-explicit-break-or-fallthrough" that will
   require explicit "break;" or "fallthrough;". The purpose of this
   switch is to make porting C apps easier.

 That way you could write native D apps without writing the tedious break
 statement. After all, as Philippe said, the behavior in 9 out of
 10 cases is to break. The superfluous breaks just clutter up the code.

 If you'd need to port C apps, just use the option
 -require-explicit-break-or-fallthrough and insert "fallthrough" where
 there is no break. After the code compiles, you can remove the option
 and works correctly. There will be a couple of redundant break
 statements but they won't hurt anyone.

 -Antti

I agree with that solution where the compiler could find potential errors when porting code or for people that have learn that other language want to avoid common pitfall (here would think it fall through). OTOH I think that requiring both break and nobreak (or fallthrough) would be better since it remove the need of an extra compiler option that some will want to uses anyway (like those that compile at level 4 under Visual C++ even if it give far too much warning for correct code...)
Sep 16 2003
prev sibling parent reply John Boucher <John_member pathlink.com> writes:
I like the idea too (and I know C# does it), but I wonder if there are fewer
errors caused by this than by  if ( x = y )  which Walter has done away with,
although I don't entirely agree with the way he did.

In article <bk5o47$15o3$1 digitaldaemon.com>, Charles Sanders says...
I like the idea of a nobreak statement ( this has been a popular topic) , I
think the reason for keeping this is backward compatibility ( on a user
level ).

I always hated this behavior, and could never understand why this was the
default.  Can we change it ?  Taking a vote might be helpful to Walter.  I
vote we fix this beast once and for all!

Charles

"Ian Woollard" <Ian_member pathlink.com> wrote in message
news:bk5nri$14cm$1 digitaldaemon.com...
 Just had a quick look at the switch statement.

 I noticed the syntax is:

 switch (foobar)
 {
 case 0:
 dostuff();
 // I've forgotten to break; all is lost.
 case 1:
 domorestuffIdidntreallywantto();
 break;
 default:
 }

 This isn't such a good idea. The default behaviour is really unsafe. I

 suggest a 'nobreak' statement for cases where you really do want the code

 fall through; otherwise  the compiler probably should fail to compile

 switcheable off with a compiler option.)

 e.g.

 switch (foobar)
 {
 case 0:
 dostuff();
 nobreak; // we fall through, but that's fine.
 case 1:
 case 2: // note no nobreak required (although I wouldn't cry if it was)
 domorestuff();
 break;
 default:
 }

 Anyway, just an idea; probably a very good one IMNHO though.

 You might also consider the same idea in empty if and for statements.

 -Ian


John Boucher -- Quite contrary The King had Humpty pushed.
Sep 15 2003
parent reply "Charles Sanders" <sanders-consulting comcast.net> writes:
 I like the idea too (and I know C# does it), but I wonder if there are

 errors caused by this than by  if ( x = y )  which Walter has done away

 although I don't entirely agree with the way he did.

Good point, I love the assignment's in conditionals too. That was one of the problems I had with python. Walter what can we do ( if anything ) to convince you to allow this and the nobreak ? Charles "John Boucher" <John_member pathlink.com> wrote in message news:bk5pkl$1a5p$1 digitaldaemon.com...
 I like the idea too (and I know C# does it), but I wonder if there are

 errors caused by this than by  if ( x = y )  which Walter has done away

 although I don't entirely agree with the way he did.

 In article <bk5o47$15o3$1 digitaldaemon.com>, Charles Sanders says...
I like the idea of a nobreak statement ( this has been a popular topic) ,


think the reason for keeping this is backward compatibility ( on a user
level ).

I always hated this behavior, and could never understand why this was the
default.  Can we change it ?  Taking a vote might be helpful to Walter.


vote we fix this beast once and for all!

Charles

"Ian Woollard" <Ian_member pathlink.com> wrote in message
news:bk5nri$14cm$1 digitaldaemon.com...
 Just had a quick look at the switch statement.

 I noticed the syntax is:

 switch (foobar)
 {
 case 0:
 dostuff();
 // I've forgotten to break; all is lost.
 case 1:
 domorestuffIdidntreallywantto();
 break;
 default:
 }

 This isn't such a good idea. The default behaviour is really unsafe. I

 suggest a 'nobreak' statement for cases where you really do want the



to
 fall through; otherwise  the compiler probably should fail to compile

 switcheable off with a compiler option.)

 e.g.

 switch (foobar)
 {
 case 0:
 dostuff();
 nobreak; // we fall through, but that's fine.
 case 1:
 case 2: // note no nobreak required (although I wouldn't cry if it was)
 domorestuff();
 break;
 default:
 }

 Anyway, just an idea; probably a very good one IMNHO though.

 You might also consider the same idea in empty if and for statements.

 -Ian


John Boucher -- Quite contrary The King had Humpty pushed.

Sep 15 2003
parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 I like the idea too (and I know C# does it), but I wonder if there are

 errors caused by this than by  if ( x = y )  which Walter has done away

 although I don't entirely agree with the way he did.

Good point, I love the assignment's in conditionals too. That was one of the problems I had with python. Walter what can we do ( if anything ) to convince you to allow this and the nobreak ? Charles

Uses := for assignment instead of =... := would always be required in conditional... and IMO should always be required but we could allows = where it is safe to assumes that the user want to do an assignment. int a = b; // OK to uses = here a = b; // could be OK here since the result is not used. (a = b).callAMember(); // would be ok if member is not also a // member of bool. if (a := b) { } // Here := is required (or extra ())
Sep 16 2003
parent reply John Boucher <John_member pathlink.com> writes:
In article <bk7ev1$alu$1 digitaldaemon.com>, Philippe Mori says...
 I like the idea too (and I know C# does it), but I wonder if there are

 errors caused by this than by  if ( x = y )  which Walter has done away

 although I don't entirely agree with the way he did.

Good point, I love the assignment's in conditionals too. That was one of the problems I had with python. Walter what can we do ( if anything ) to convince you to allow this and the nobreak ? Charles

Uses := for assignment instead of =... := would always be required in conditional... and IMO should always be required but we could allows = where it is safe to assumes that the user want to do an assignment. int a = b; // OK to uses = here a = b; // could be OK here since the result is not used. (a = b).callAMember(); // would be ok if member is not also a // member of bool. if (a := b) { } // Here := is required (or extra ())

Uh, I hope I didn't suggest having two different assignment operators to do the same thing, with one being used in tests and the other one not. That's just wrong. I _did_ suggest replacing the bare equal sign (=) assignment operator with a colon-equal (:=) a la Pascal, and I stand by that. To anger several of you further allow me to go several steps farther over the edge and suggest redoing all the assignment operators to include a colon... = becomes := += becomes :+ -= becomes :- *= becomes :* /= becomes :/ %= becomes :% <<= becomes :<<
= becomes :>>


&= becomes :& |= becomes :| ^= becomes :^ ~= becomes :~ Did I forget any? If not for D, then for the language that is sure to come after D. Oh, oh, oh -- I just had an even whackier idea... Howsabout we require something to tell the compiler that we really do want to do the assignment in the test? Like: [safe] if ( x = y ) ... ; Kinda sorta like C#. John Boucher The King had Humpty pushed.
Sep 16 2003
parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
Uses := for assignment instead of =...

:= would always be required in conditional... and IMO should always
be required but we could allows = where it is safe to assumes that
the user want to do an assignment.

int a = b;    // OK to uses = here

a = b;    // could be OK here since the result is not used.

(a = b).callAMember();    // would be ok if member is not also a
                                        // member of bool.

if (a := b) { }    // Here := is required (or extra ())

Uh, I hope I didn't suggest having two different assignment operators to

 same thing, with one being used in tests and the other one not. That's

 wrong.

It is the same operator... just that the short version is more restricted... but if := is always used, it is OK for me... But still allowing = would allows easier porting of existing code as := would be seldom required...
 I _did_ suggest replacing the bare equal sign (=) assignment operator with

 colon-equal (:=) a la Pascal, and I stand by that. To anger several of you
 further allow me to go several steps farther over the edge and suggest

 all the assignment operators to include a colon...

 =   becomes :=
 +=  becomes :+
 -=  becomes :-
 *=  becomes :*
 /=  becomes :/
 %=  becomes :%
 <<= becomes :<<
= becomes :>>


&= becomes :& |= becomes :| ^= becomes :^ ~= becomes :~ Did I forget any?

 If not for D, then for the language that is sure to come after D.

I think that != should not be here. Does != means not equal (i.e. <>). This is a comparison and not an assignment!!! I think those changes are essentially useless and will make porting code a lot harder... And they are less visible (= is easier to spot than : in code)
 Oh, oh, oh -- I just had an even whackier idea...
 Howsabout we require something to tell the compiler that we really do want

 the assignment in the test? Like:
 [safe]
 if ( x = y ) ... ;

 Kinda sorta like C#.

If we have such a think, then we would like to be able to uses it in other context where like: - comparison or assignment between signed/unsigned - possible truncation - no break in a switch case (hey I'm back on switch discussion!) - any other warnings What I dislike with your solution is that it is not possible to apply the attribute only to a part on an expression... so in complex situations we might uses safe for one thing and hide another problem... In such a case, it would be better to have operator prefix (:, , #, $,?,...) that would modify the warnings that the compiler would normally give: We could have for example as prefix: : This is an assignment operator prefix ? This is a conditionnal operator prefix # This is a restricted (safe) operator prefix (for ex. disallow some implicit conversion) This is a free (unsafe) operator prefix (allows more conversions like double to int, most anything to bool, base to derived...) $ Handle null object Some examples: if (a $== b) { } // Ok even if a, b or both are null if (a ?= b) { } // Is a equal to b if (a := b) { } assignment and test int i; double d; unsigned u; i = d; // Ok, allows truncation and other less safe conversion if (i == u) { } // No warning for comparing signed/unsigned -u; // No warning for negating an unsigned. base[] ba; derived[] de; ba #= de; // Not allowed (want to be safer than usual) d #= i; // Not allowed (idem) Note that some case like just above are not very usefull for regular code but might be interesting in template code where we might be more or less restrictive in some cases. Also, we might support # and for functions arguments to indicate if we we want to be safer than usual (almost no conversion allowed) or allows more automatic conversions to happen automatically (for example converting a double to an int as in: void f( int i) { ... } f(25.5);
 John Boucher
 The King had Humpty pushed.

Sep 16 2003
parent John Boucher <John_member pathlink.com> writes:
In article <bk84fu$2kbi$1 digitaldaemon.com>, Philippe Mori says...
Uses := for assignment instead of =...

:= would always be required in conditional... and IMO should always
be required but we could allows = where it is safe to assumes that
the user want to do an assignment.

int a = b;    // OK to uses = here

a = b;    // could be OK here since the result is not used.

(a = b).callAMember();    // would be ok if member is not also a
                                        // member of bool.

if (a := b) { }    // Here := is required (or extra ())

Uh, I hope I didn't suggest having two different assignment operators to

 same thing, with one being used in tests and the other one not. That's

 wrong.

It is the same operator... just that the short version is more restricted... but if := is always used, it is OK for me... But still allowing = would allows easier porting of existing code as := would be seldom required...
 I _did_ suggest replacing the bare equal sign (=) assignment operator with

 colon-equal (:=) a la Pascal, and I stand by that. To anger several of you
 further allow me to go several steps farther over the edge and suggest

 all the assignment operators to include a colon...

 =   becomes :=
 +=  becomes :+
 -=  becomes :-
 *=  becomes :*
 /=  becomes :/
 %=  becomes :%
 <<= becomes :<<
= becomes :>>


&= becomes :& |= becomes :| ^= becomes :^ ~= becomes :~ Did I forget any?

 If not for D, then for the language that is sure to come after D.

I think that != should not be here. Does != means not equal (i.e. <>). This is a comparison and not an assignment!!! I think those changes are essentially useless and will make porting code a lot harder... And they are less visible (= is easier to spot than : in code)
 Oh, oh, oh -- I just had an even whackier idea...
 Howsabout we require something to tell the compiler that we really do want

 the assignment in the test? Like:
 [safe]
 if ( x = y ) ... ;

 Kinda sorta like C#.

If we have such a think, then we would like to be able to uses it in other context where like: - comparison or assignment between signed/unsigned - possible truncation - no break in a switch case (hey I'm back on switch discussion!) - any other warnings What I dislike with your solution is that it is not possible to apply the attribute only to a part on an expression... so in complex situations we might uses safe for one thing and hide another problem... In such a case, it would be better to have operator prefix (:, , #, $,?,...) that would modify the warnings that the compiler would normally give: We could have for example as prefix: : This is an assignment operator prefix ? This is a conditionnal operator prefix # This is a restricted (safe) operator prefix (for ex. disallow some implicit conversion) This is a free (unsafe) operator prefix (allows more conversions like double to int, most anything to bool, base to derived...) $ Handle null object Some examples: if (a $== b) { } // Ok even if a, b or both are null if (a ?= b) { } // Is a equal to b if (a := b) { } assignment and test int i; double d; unsigned u; i = d; // Ok, allows truncation and other less safe conversion if (i == u) { } // No warning for comparing signed/unsigned -u; // No warning for negating an unsigned. base[] ba; derived[] de; ba #= de; // Not allowed (want to be safer than usual) d #= i; // Not allowed (idem) Note that some case like just above are not very usefull for regular code but might be interesting in template code where we might be more or less restrictive in some cases. Also, we might support # and for functions arguments to indicate if we we want to be safer than usual (almost no conversion allowed) or allows more automatic conversions to happen automatically (for example converting a double to an int as in: void f( int i) { ... } f(25.5);
 John Boucher
 The King had Humpty pushed.


Wow, good ideas. And I misspoke with the !=, I knew something was wrong. I don't mean to change the not-equal-to operator, I mean to add an "assign the not of" operator, my bad. John Boucher -- Self-contrary The King had Humpty pushed
Sep 16 2003
prev sibling next sibling parent reply "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Ian Woollard" <Ian_member pathlink.com> ha scritto nel messaggio
news:bk5nri$14cm$1 digitaldaemon.com...
 [...] The default behaviour is really unsafe. I would
 suggest a 'nobreak' statement for cases where you really do want the code

 fall through; otherwise  the compiler probably should fail to compile

 switcheable off with a compiler option.)

I vote for that too. And BTW, Walter, while we're on the subject... how about multiple-value case labels, a la VB? switch (x) { case 0: ... case 1, 3, 5: ... case 2, 4, 6 to 10: // or 6..10 as in Pascal, or [6, 10] or whatever ... case is < 1: // see comments on this one below ... case 11, 13 to 15, is > 16: // All together now! ... default: // no, I'm not proposing a "case else" :) } While all other forms are simply short for writing more labels, the "is" form really requires the switch to be compiled as it was a series of if's, with the difference that part of the if expression has already been evaluated. The syntax would be "is <relational operator> <expression>" If the switched-on expression is an object, overloaded operators might be used (provided it's not already so); in this case, "case is <obj_reference_or_null>" would compare pointers instead of checking object equality. Note that I'm following the "is" operator proposal here... OK, just daydreaming maybe... I suppose that optimizing such a thing would be rather hard... anyway I tried... Ric
Sep 16 2003
next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
[...] The default behaviour is really unsafe. I would
suggest a 'nobreak' statement for cases where you really do want the code
to
fall through; otherwise  the compiler probably should fail to compile
(possibly
switcheable off with a compiler option.)

I vote for that too.

I'd like to see a nobreak statement, but I absolutely hate to see a compiler option that basically changes the language. If a D program is valid then it should always be valid. And if it is not then the compiler shouldn't accept it, no matter what switches you pass. Otherwise we will end up with two different types of D programs (the auto-fall-through and the nobreak-required one) that are not source compatible. Stuff like that should be avoided from the very beginning, so there should be a definite decision on wether nobreak is required or not.
 And BTW, Walter, while we're on the subject... how about multiple-value case
 labels, a la VB?

I think that is a nice idea. And it would also make the nobreak requirement less of a hassle, since most fall through cases are really multi-value cases (i.e. the same code body for all cases).
 switch (x)
 {
 case 0:
     ...
 case 1, 3, 5:
     ...
 case 2, 4, 6 to 10:  // or 6..10 as in Pascal, or [6, 10] or whatever
     ...
 case is < 1:  // see comments on this one below
     ...

Is the last 'case' consistent with the proposed use of the "is" operator as a replacement for ===? Wouldn't this mean that if x is an object, then the pointer would be compared with 1?
 If the switched-on expression is an object, overloaded operators might be
 used (provided it's not already so); in this case, "case is
 <obj_reference_or_null>" would compare pointers instead of checking object
 equality. Note that I'm following the "is" operator proposal here...

One thing to consider is that case statements involving objects and overloaded operators make it impossible for the compiler to check wether they are non-overlapping. E.g. class X; X c1; X c2; X c3; switch(somevalue) { case < c1: ... break; case [c2..c3]: ... break; } How should the compiler check wether there isn't a value v < c1 that is also >=c2 and <=c3? Now that I think more about it, I think retrofitting switch to become as powerful as if is probably not a good idea at all. Lets face it, the switch statement is really only an optimization to help the compiler generate efficient code. If switch was as powerful as if, then there would be no additional optimization possibilities and the whole statement would be pretty useless. [Oh the irony ;)]. Hauke
Sep 16 2003
next sibling parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bk6n5l$11j6$1 digitaldaemon.com>, Hauke Duden wrote:
 Now that I think more about it, I think retrofitting switch to become as 
 powerful as if is probably not a good idea at all. Lets face it, the 
 switch statement is really only an optimization to help the compiler 
 generate efficient code. If switch was as powerful as if, then there 
 would be no additional optimization possibilities and the whole 
 statement would be pretty useless. [Oh the irony ;)].

Of course, you *could* make the switch statement a generalization of the old one without losing optimizations. Just define switch so that switch (<expr>) { case <c11>, <c12>: <statement1> case <c2>: <statement2> ... default: <statementn> } is be semantically equivalent to (ignoring fallthroughs for a moment, they'd have to be implemented with gotos or sth like that) typeof <expr> tmp = <expr>; if ((tmp == <c11>) || (tmp == <c12>)) { <statement1> } else if (tmp == <c2>) { <statement2> } else { <statementn> } The compiler will in this case have to recognize the "old-style" switch statements (where all cases are constant and maybe not overlapping), which should be trivial, and perform the usual optimizations on them. More complex statements (where some case is non-constant) are implemented as an if-elseif-etc-else statement. -Antti
Sep 16 2003
next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Antti Sykäri wrote:
 Of course, you *could* make the switch statement a generalization of the
 old one without losing optimizations.

<snip>
 The compiler will in this case have to recognize the "old-style" switch
 statements (where all cases are constant and maybe not overlapping),
 which should be trivial, and perform the usual optimizations on them.
 More complex statements (where some case is non-constant) are
 implemented as an if-elseif-etc-else statement.

I don't know. To me this seems to be a feature that complicates the the language (and the compiler) with not much of a real benefit. If you need if-capabilities, why not use if? You might need a temporary variable to store the "switch value", but that is really the only downside I see. Upsides of leaving the switch-case statement simple are: - less complicated compiler - no need to invent new syntax to express <,>,<=, is-in-interval [a..b], ]a..b], [a..b[, ]a..b[ etc. - You can instantly be certain that you are looking at "flat" code (i.e. code where no operator overloads and stuff like that are involved) when you see a switch statement. I still think a multi-case statement would be nice, though. It would only be syntactic sugar for multiple cases with a fallthrough, of course, but this is something that would probably actually be useful in everyday programming. Especially if "nobreak" is required to fall through to the next case statement. Hauke
Sep 16 2003
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bk6vo2$1sdp$1 digitaldaemon.com>, Hauke Duden wrote:
 Antti Sykäri wrote:
 Of course, you *could* make the switch statement a generalization of the
 old one without losing optimizations.

<snip>
 The compiler will in this case have to recognize the "old-style" switch
 statements (where all cases are constant and maybe not overlapping),
 which should be trivial, and perform the usual optimizations on them.
 More complex statements (where some case is non-constant) are
 implemented as an if-elseif-etc-else statement.

I don't know. To me this seems to be a feature that complicates the the language (and the compiler) with not much of a real benefit. If you need if-capabilities, why not use if? You might need a temporary variable to store the "switch value", but that is really the only downside I see.

The compiler might be slightly more complicated, or then again it could actually be even simpler or as simple. A quick'n'dirty compiler might implement "switch" simply by translating it to an if-else sequence, and an optimizing compiler could do as the optimizing C compilers do, if all values in case statements are known at compile time. On the other hand: - there would be less problems with: - "Why can't I use struct values/classes/enums/your-arbitrary-expression-here in the switch statement?" - And what the heck is so special about those that they can't be evaluated at runtime like everything else? - added ease for the maintenance programmer... For example, consider that you have an interactive environment which is used to "frobnicate" and "gref" and the older version also supported some features that now are deprecated. You might write a simple interpreter for it like this: char[] kw = read_keyword(); switch (kw) { case "frobnicate": do_frob(); break; case "gref": do_gref(); break; case "quit": quit(); break; case "xixaxa_xoxaxa_xuxaxa": // FALL THROUGH case "kirje": printf("Sorry, command %d is no longer supported.\n", kw); print_help(); break; default: printf("Unknown command %d\n", kw); break; printf_help(); } Interpreter works fine. Now your manager comes into the picture and says: "I need that localized in 10 languages, ASAP. Oh yeah, the keywords need to be localized too." So you decide to localize the whole codebase by first automatically extracting all the strings that exist in the program (say, 100000 lines) with a perl script and then search-and-replacing each string, for example, "xyzzy" with localize("xyzzy"). localize() is a global function that replaces xyzzy with whatever is appropriate in the current locale. "Yeah that's simple", you say to your manager, "I'll take at most a couple of hours to do the search & replace and write the localization code" Then you do it (15 minutes it takes), and result is: switch (kw) { case localize("frobnicate"): dothis(); break; case localize("gref"): ...etc... } You start doing manual, repetitive, error-prone work: converting switch statements to if-else statements. At the end of the week your boss asks "What took you so long? By the way, we need the localization in an other branch of the software delivered to customer X two months ago, but I guess it's simple to do since you already did it" and there we go again... This is just an extreme example of how orthogonal language features can be useful. Sometimes real life can be extreme, too. To summarize, what I hoped earlier was that the mechanics of the "switch" statement would be driven by its semantics rather than its implementation (switch is often compiled as a table lookup, using linear search, hashing or even binary search, and for that reason they have been restricted to constant values of basic types -- because it's easiest to produce efficient code for them. Dunno what D compiler does with string literal lookups, though -- is it a trade secret? <grin>). I admit that being driven by implementation can make you feel sort of close to machine, which is sort of a good thing. Efficient-looking constructs often end up being efficient, too. This is part of the fascination in C. But sometimes you want things that Just Work, and we seem to be steadily traveling away from the bare metal anyway. I'm not suggesting making "switch" work on non-constant values would be a critical feature in the language, just some extra cream on the cake. This issue also touches things like reference to bit[], template arguments of different types, what-have-you. There should be as few special cases as possible.
 Upsides of leaving the switch-case statement simple are:
 
 - less complicated compiler
 - no need to invent new syntax to express <,>,<=, is-in-interval [a..b], 
 ]a..b], [a..b[, ]a..b[ etc.

Sorry if you got me wrong, I didn't mean to propose those. I believe the language has too much syntax already. Adding extra operators feels the same as adding new icons to your favorite Windows program's toolbar. Some Windows programs that I use have tons of tiny icons and I don't have the faintest of what they actually do. Just having a button with a caption would suit me fine, thank you. Or, in the programming language area, a method or function which does the same. For example, "x is_in_interval [1..5]" might mean 1 <= x && x <= 5 (or < 5 if you want it the Dijkstra way) in a hypothetical programming language. Why not have [1..5] denote a constant of type Range, and have a method Range.has(int)? Then you'd have "[1..5].has(x)". But why end there if you can have "Range(1, 5).has(x)". (Well, maybe operators are sometimes nice after all.)
 - You can instantly be certain that you are looking at "flat" code (i.e. 
 code where no operator overloads and stuff like that are involved) when 
 you see a switch statement.

Hmm, strangely I have no special desire to be certain that there wouldn't be operator overloading or other features that belong into a modern language. I should be able to trust that the operators I use are overloaded properly (so that they convey the right semantics). Oops, what a rant this grew up to be... better stop and do something useful for a while ;) -Antti
Sep 16 2003
next sibling parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 Then you do it (15 minutes it takes), and result is:

     switch (kw)
     {
     case localize("frobnicate"): dothis(); break;
     case localize("gref"):
     ...etc...
     }

Here I suppose you would like that every items be compared in turn for equality with kw until a match is found... And what we should do if I want to uses something else than operator== for the comparison ? It would then be nice to be able to pass a function/delegate/function object/... that would be used for the comparison similat to what we have in C++ STL where many algorithm accept user predicate...
 This is just an extreme example of how orthogonal language features can
 be useful. Sometimes real life can be extreme, too.

 To summarize, what I hoped earlier was that the mechanics of the
 "switch" statement would be driven by its semantics rather than its
 implementation (switch is often compiled as a table lookup, using linear
 search, hashing or even binary search, and for that reason they have
 been restricted to constant values of basic types -- because it's
 easiest to produce efficient code for them. Dunno what D compiler does
 with string literal lookups, though -- is it a trade secret? <grin>).

Well if this is allowed then it mean that the compiler would be able to find duplicate case which is a usefull feature of case... and I'm not sure that having different rules on how it works depending on weither the expression is a constant or not is a good idea... This simple change allows for bugs that would be caused by misusing a variable a constant (for example using a enum variable instead of a value defined inside of the enum). If switch is extended in such a way, a would prefer a solution where would have to explictly specify a function for comparing the values: bool myfunction(char[] expr, char[] caseValue) { return localize(caseValue) == expr; } switch (x, myfunction) // I'm not sure of the syntax for passing a function // or a delegate or sothing similar { case "unlocalized1" : break; case "unlocalized2" : break; } That way, we would be able to localize without replacing the switch but global find and replace won't works either... But that syntax has some limitations: First it won't works for range or condition like < 5. Second, we can check only one thing at a time... So while it is better, the solution is not as general as someone could like it.... And I think it would be preferable to have a syntax that allows ranges like < 5 or 1..5. Maybe we should always uses a..b syntax and uses special constant min/max or -INF/+INF for range that are not bounded on one side: case min..5 : case -INF..5 : Also it would be interesting to support range that include/exclude limit for types that are not finite (double, strings,...). Maybe, [, (, ) and ] should be used as required... or we might have a postfix + or - to indicate if the value is include or not: case 5+..9.5+ : // from 5 (not included to to 9.5 included) case 5..9 : // 5 to 9 both ends included case 5-..9+ : // Idem case 5+..9-- : // 5 to 9, ends not included
 For example, "x is_in_interval [1..5]" might mean 1 <= x && x <= 5 (or <
 5 if you want it the Dijkstra way) in a hypothetical programming
 language. Why not have [1..5] denote a constant of type Range, and have
 a method Range.has(int)? Then you'd have "[1..5].has(x)". But why end
 there if you can have "Range(1, 5).has(x)". (Well, maybe operators are
 sometimes nice after all.)

If we allows something like Range(1, 5).has(x), then it means that when we look at the switch expression, we will never be sure that only that is used to evaluate which case to select... since one could uses a function with side effect or reference a variable that is not is the switch expression. IMO we should only extent switch to support range and support any type of constant (if this is not already the case). The main difference between if/else and a switch is that a switch is supposed to select a case from an expression... And I thing that we should be able to have arbitrary expression for each case as this would allows side-effect and subtle bugs... caused by the fact that the cases do not depend only on the expression... Also, I think that a switch should be independant on the order of each case and reordering them should not changes the output but might only affect the performance if the optimizer assumes that first cases are more probable...
Sep 16 2003
prev sibling next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Antti Sykäri wrote:
 The compiler might be slightly more complicated, or then again it could
 actually be even simpler or as simple. A quick'n'dirty compiler might
 implement "switch" simply by translating it to an if-else sequence, and
 an optimizing compiler could do as the optimizing C compilers do, if all
 values in case statements are known at compile time.

You argue your case well ;). I can now see the benefits of having a generalized switch statement.
 Sorry if you got me wrong, I didn't mean to propose those. I believe the
 language has too much syntax already.

Agreed.
 For example, "x is_in_interval [1..5]" might mean 1 <= x && x <= 5 (or <
 5 if you want it the Dijkstra way) in a hypothetical programming
 language. Why not have [1..5] denote a constant of type Range, and have
 a method Range.has(int)? Then you'd have "[1..5].has(x)". But why end
 there if you can have "Range(1, 5).has(x)". (Well, maybe operators are
 sometimes nice after all.)

This raises a new problem, though. To be able to apply complicated operators with the current condition-syntax you would have to have some special constant that represents the switch-value. I.e. something like: switch(someFunc()) { case switchval>=5 && switchVal<=9: do something... } However, this might make nested switch statements a little hard to understand. switch(someFunc()) { case switchval>=5 && switchVal<=9: switch(someOtherFunc()) { case switchVal.coolCheckFunc(): /*this is the NEW value*/ someStuff(); break; } break; } Add some more nested layers and things are certain to get complicated quickly. I think if you wanted to do something like this you'd have to be able to specify a name for the switch value. Like an imaginary const variable. Something like this: switch(someFunc() as sval1) { case sval1 >= 5 .... } Hauke
Sep 17 2003
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bk97c1$1dfi$1 digitaldaemon.com>, Hauke Duden wrote:
 Antti Sykäri wrote:
 The compiler might be slightly more complicated, or then again it could
 actually be even simpler or as simple. A quick'n'dirty compiler might
 implement "switch" simply by translating it to an if-else sequence, and
 an optimizing compiler could do as the optimizing C compilers do, if all
 values in case statements are known at compile time.

You argue your case well ;).

I try not to post only my opinions but also the thinking process behind them. This is because I've found that suggestions and propositions that have no convincing arguments behind them tend to be easily dismissed. The result is what you see also in this posting: longish rants :) (Sorry, I've yet to learn the skill of Concise Ranting!)
 I can now see the benefits of having a generalized switch statement.

But the question remains, how generalized? In my previous posting, I talked about generalizing switch; now I'll wear another hat and present some arguments for restricting it. I believe that the switch statement could be generalized to some extent. But it must still stay limited: it should fundamentally play the same role as the classic "switch". My view is that the meaning of switch should be "evaluate an expression, and based on the result, select one of the legs and execute its code". Whether the values that the result is compared against are constant -- or even if they are distinct -- is to me irrelevant. Actually, when you think of it, even falling through is a pretty useless idea. In the good old C language, falling through was not just a hack but very useful hack indeed. It was an easy way to get multiple values to do the same thing. A modern language might want to replace falling through with switching on multiple values, either in the set form or in the range form: (syntax would be hypothetical here) case 1, 2, 3: do_this() or case 1..4, 9: do_that() or even, if we have the dynamical version List(int) some_numbers; some_numbers.insert(1, 2, 3); Set(int) other_numbers; other_numbers.insert(1,2,3,4,9); case some_numbers: do_this() case other_numbers: do_that() List(T) and Set(T) should of course be the D's natural representations for those concepts. Or maybe just any classes that model a certain concept: it should have a "contains" or "has" method which checks the existence of a value in itself. Maybe I'm getting away from the planet earth a bit too fast but hey -- if you accepted things like "case 1, 2, 3" and ranges, why not accept dynamic sets and ranges as well? I'm not really sure of this. It might look like a crude hack to treat sets, ranges and values the same, but my judgement fails me here... Let's consider this thing for a second. Well, range and set are ok. Range is just a specialized sort of set. You can say "(1..5).has(1)", and "Set(int) x = makeSet(1, 2); x.has(1)", and it makes sense. But can you do 1.has(1)? Obviously testing of membership (or equivalence) would have to be like in the foreach case - compiler magic if it's a basic type, and applying of function such as "has()" or "equals()"/"==" if it's a user-defined type. But which one to apply? Use operator "==" only if the combination has no matching "has" method? Use "has" only if there is no matching operator "=="? If neither matches, it's an error -- what if both match? It might be a dark corner in the language; reminds me of Russell's paradox in mathematics ("Define a set "well-behaved" if it doesn't contain itself as an element. Consider the set S of all well-behaved sets. Is S well-behaved?"). Or then again it might cause no harm at all in practice. (Designers of popular languages, starting from C, have always known when to cut corners.) We were talking about fallthrough, right? Someone might point out that fallthrough has other uses besides doing the thing X on several values: occasionally you could even "reuse" some code by first doing something in the first leg and then continuing to the second one. But the value of this is dubious at least from the viewpoint of code maintenance. The programmer that follows your footsteps is likely to add another leg between those two. Murphy's laws state that the bug will manifest itself the day when he quits the company and there we have it: a maintenance nightmare. Nowadays a good programmer would probably extract the common code to a function (or to a local function as we have such a splendid feature in D) and avoid the problem. Oh what the heck, maybe you could leave fallthrough there -- when you try to prohibit something, the programmers will work hard trying to dig around the limitations and the result often ends up even dirtier than expected. (In this case I can't imagine anything uglier than a goto statement to the next leg. I'm certain, though, that there are more talented programmers out there who can crank out something so horrible and unmaintainable that it would truly be a work of art.) Now about the distinctness of the values-to-be-checked-against of the switch statement, mentioned in another follow-up. Originally it was also useful to enforce distinctness of the different values on which to switch upon: including two or more switch legs with the same constant value would be pointless and misleading. (And ill-defined as long as the semantics are defined as "THE switch statement that matches X will be executed") Although this view can be also disputed. I recently had a switch statement similar to: switch (keycode) { case middle_button: case fire: case some_other_button: ... } On platform X, middle_button and fire were on different keys, and, consequently, keycodes. However, on platform Y, there were no special "middle button" so the mappings were changed and "fire" ended denoting to the same integer value as "middle_button". Compile-time error was the result. So this was worked around along the lines of: switch (keycode) { case middle_button: #ifndef PLATFORM_Y case fire: #endif case some_other_button: ... } As you can see it's not a pretty solution. And then some comments:
 This raises a new problem, though. To be able to apply complicated 
 operators with the current condition-syntax you would have to have some 
 special constant that represents the switch-value.
 
 I.e. something like:
 
 switch(someFunc())
 {
 case switchval>=5 && switchVal<=9:	do something...
 }

Oh yeah, now let's reiterate that the current posting is all about "I didn't want to do that much after all. Just a little bit syntactic would be fine and then I be nice and quiet" ;-) Seriously, I wouldn't necessarily go as far as to allow _any_ expression to occur in a case "label". It would make the usual cases (equivalence and belonging into a set, another container, or a range) too wordy and the whole switch statement merely a if-elseif-then in a disguise. But it's a good idea to have access to the statement-to-be-switched. Now that I think about it, I've often wanted access to it.
 However, this might make nested switch statements a little hard to 
 understand.
 
 switch(someFunc())
 {
 case switchval>=5 && switchVal<=9:	
 	switch(someOtherFunc())
 	{
 	case switchVal.coolCheckFunc():  /*this is the NEW value*/
                       someStuff();
 		break;
 	}
 	break;
 }

As an aside, did you read http://lambda.weblogs.com/discuss/msgReader$8721 (about referring to things with pronouns)? Anyway I propose the following syntax: switch (int result = someFunc()) { ... case 2, 3, 5, 7: printf("%d is a small prime\n", result); ... } that also the for loop uses and someone, I think, also proposed it for the while and/or if statements. That might be good for consistency, hint, hint :) Certainly, you can do the same if you introduce the variable immediately before the statement, but hey, why can I introduce a variable in a for statement but not elsewhere? That's one of the questions that no one should be able to ask. -Antti
Sep 17 2003
next sibling parent John Boucher <John_member pathlink.com> writes:
In article <slrnbmhp8u.bkq.jsykari pulu.hut.fi>, Antti =?iso-8859-1?Q?Syk=E4ri?=
says...
Actually, when you think of it, even falling through is a pretty useless
idea. In the good old C language, falling through was not just a hack
but very useful hack indeed. It was an easy way to get multiple values
to do the same thing.  A modern language might want to replace falling
through with switching on multiple values, either in the set form or in
the range form:


-Antti

Here you seem to refer only to falling-through from an empty case, which I agree would be made clearer by allowing multiple values on the case. One of the few reasons I _do_ use fall-through is in something like the following, where having four of something means that there are also three of it: import c.stdio ; void main ( char[][] args ) { int lines = 0 ; char[] outfile = "stdout" ; switch ( args.length ) { case 4 : { // Parse the "#lines" parameter } case 3 : { // Parse the "outfile" parameter } case 2 : { // Parse the "infile" parameter } default : { printf ( "\nYou did not specify enough parameters\nSyntax: DTEST infile [ outfile [ #lines ]]" ) ; } } return ; } John Boucher The King had Humpty pushed.
Sep 17 2003
prev sibling next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Antti Sykäri wrote:
 But the question remains, how generalized?
 
 In my previous posting, I talked about generalizing switch; now I'll
 wear another hat and present some arguments for restricting it.

<snip>
 Maybe I'm getting away from the planet earth a bit too fast but hey --
 if you accepted things like "case 1, 2, 3" and ranges, why not accept
 dynamic sets and ranges as well?
 
 I'm not really sure of this. It might look like a crude hack to treat
 sets, ranges and values the same, but my judgement fails me here...
 Let's consider this thing for a second.

You seem to alternate between wanting to have an uber-powerful switch and at the same time keep it as simple as it currently is. I have noticed the same tendency in myself. However, then I realized that switch really has only one major benefit over if-else: it is easy to express SIMPLE conditional branching. If you introduce switch-case with dynamic lists or sets you end up not only having a switch that is as powerful as if, but it becomes a foreach and if rolled into one! I really think having such "fat" statements in a programming language (even a high level one) is a bad idea. You'd end up becoming some sort of CISC language (to borrow a term from CPU design) that tries to provide direct solutions for every problem on earth. We already have two primitives (foreach and if) that can handle these cases. We should try to keep the language simple and not introduce too many ways to express the same thing. And if someone really needs this all the time then he could always write a template to do it. In my opinion this kind of specialized solution should be part of a library, not of the language. Another point against a uber-switch is that in my experience long, nested switch statements can be pretty hard to read. The syntax is just a little too different from all other constructs (no curly braces for the code blocks, no round braces for conditions, break denotes the end, etc.). And combining this with the way many people (do not) handle indentation I'd hate to see switch used everywhere where foreach and if may actually be a better choice.
 Actually, when you think of it, even falling through is a pretty useless
 idea. In the good old C language, falling through was not just a hack
 but very useful hack indeed. It was an easy way to get multiple values
 to do the same thing.  A modern language might want to replace falling
 through with switching on multiple values, either in the set form or in
 the range form:

As another post in this thread noted, fallthrough CAN sometimes be useful. However, I agree that these cases are pretty rare and multi-cases could replace the need for fallthrough in many circumstances. Maybe it isn't such a bad idea to just kick out fallthrough completely. The few instances where multi-cases aren't adequate could still easily be solved with if-else and overall readability would be improved. About your idea of allowing non-distinct case-statements: I'm not sure this is such a good idea. I guess in many cases non-distinctness would be a bug (say, using the wrong constant). Silently executing more than one branch just seems unintuitive to me. If we wouldn't have the requirement to easily port C/C++ code to D (do we really have it?) I would like to suggest to ditch the classic switch statement completely and introduce a new construct with different syntax that is more consistent with the rest of the language. This would also solve the problem that different fallthrough behaviour might confuse C/C++ programmers, since it is immediately recognizable as a different thing. Maybe something like this: branch(expression) { on(1) someFunc(); on(2,3,5..8) { a=7; dostuff(a,true); } } Hmmm. I think the syntax would probably need some work, but it would have some benefits: - the on-blocks are real code blocks. Adding local variables to them works in the same way as it does anywhere else. - break is not used in this construct, so it could actually be used to break out of an outer loop. This is something that I quite often wanted to do in C++. Ok, it is not THAT much of an issue in D anymore, since we can break with a label but being able to do it without an explicit label improves readability, since it is immediately clear which loop is referred to. But since I don't see any chance that switch is ditched there is no real point to argue for such a statement anyway ;). About having a name for the switch value:
 As an aside, did you read http://lambda.weblogs.com/discuss/msgReader$8721 
 (about referring to things with pronouns)?
 
 Anyway I propose the following syntax:
 
 switch (int result = someFunc())
 {
     ...
     case 2, 3, 5, 7: printf("%d is a small prime\n", result); ...
 }

I thought about something like that too. However, this would introduce a real variable, not just a name for a value. There may be some performance issues with that. Also, you'd have to make this variable const to prevent it from being changed inside the switch statement => more things to check for the compiler. Hauke
Sep 18 2003
parent reply Erik Baklund <Erik_member pathlink.com> writes:
Way to go!

erik

In article <bkbunb$29u9$1 digitaldaemon.com>, Hauke Duden says...

<snip>
Maybe it isn't such a bad idea to just kick out fallthrough completely.
The few instances where multi-cases aren't adequate could still easily
be solved with if-else and overall readability would be improved.

<snip>
If we wouldn't have the requirement to easily port C/C++ code to D (do
we really have it?) I would like to suggest to ditch the classic switch
statement completely and introduce a new construct with different syntax 
that is more consistent with the rest of the language. This would also
solve the problem that different fallthrough behaviour might confuse 
C/C++ programmers, since it is immediately recognizable as a different 
thing.

Maybe something like this:

branch(expression)
{
	on(1)	someFunc();
	on(2,3,5..8)
	{
		a=7;
		dostuff(a,true);
	}
}

Sep 18 2003
parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 Way to go!

I would like to suggest to ditch the classic switch
statement completely and introduce a new construct with different syntax
that is more consistent with the rest of the language. This would also
solve the problem that different fallthrough behaviour might confuse
C/C++ programmers, since it is immediately recognizable as a different
thing.

Maybe something like this:

branch(expression)
{
 on(1) someFunc();
 on(2,3,5..8)
 {
 a=7;
 dostuff(a,true);
 }
}


I also like that syntax... Clear, no problem with it (fall-through) and enough different... And if in some situation we need more power, we should also provide a distinct syntax for that feature instead of have the same keyword used for too much things (like static in C++). On thing that I would like is the ability to specify that every possible values must be handled in the branch statement so that if a value is added to an enum, we would be able to have an error for all branch that do not handled that new value... and we also need the default case... So we have 3 possibilities: - partial set of cases checked - partial set of cases checked and a default - all cases must be checked (implies no default) ondefault for default case or maybe else section: exclusive_branch(expression) same as above but all cases must be present Maybe we should still support the old switch for C compatibility but uses branch whenever possible... That way, we have best of both world, elegant modern syntax and safe when using brand, compatibility and a bit more power when using switch... (for exemple possibility of fall-through).
Sep 18 2003
parent "Sean L. Palmer" <palmer.sean verizon.net> writes:
I like that branch too.

If we had that, after a few years once D gains enough popularity, old
C-style switch support could be dropped.  ;)

Sean

"Philippe Mori" <philippe_mori hotmail.com> wrote in message
news:bkdh98$1evd$1 digitaldaemon.com...
 Maybe we should still support the old switch for C compatibility but
 uses branch whenever possible... That way, we have best of both
 world, elegant modern syntax and safe when using brand, compatibility
 and a bit more power when using switch... (for exemple possibility of
 fall-through).

Sep 19 2003
prev sibling parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 Anyway I propose the following syntax:

 switch (int result = someFunc())
 {
     ...
     case 2, 3, 5, 7: printf("%d is a small prime\n", result); ...
 }

 that also the for loop uses and someone, I think, also proposed it for
 the while and/or if statements. That might be good for consistency,
 hint, hint :)

 Certainly, you can do the same if you introduce the variable immediately
 before the statement, but hey, why can I introduce a variable in a for
 statement but not elsewhere? That's one of the questions that no one
 should be able to ask.

I agree with that extension.... For dynamic cases, it is OK provided that we still allows the compiler to validate static cases and we define the interaction between them. For maintenance nightmare, I agree that function should normally be used... but sometimes, it is easy to optimize the code just by using fallthrough but if not supported directly, one can uses goto for those rare cases where performance or code size do really matter... Also in some specific situations, it might be interesting to evaluate all cases, reevaluate the switch, goto a specific case, fallthrough,... In fact for event-driven programming where we have to handled some messages, having some controls might be interesting... but in those case, one would not only want to check a signle member for a few conditions (like the control ID and the message ID for an UI event)... And in those case, it might even be interesting to be able to return a value that indicate if the event was handled or not and having that used to decide if we fall-through the next case... In C++, event handling often uses switch or if/else with sometime a bool to indicate if the event was handled: BEGIN_MAP ON_CDM(id, fn1) ON_EXIT(fn2) ON_MESSAGE(msgId, ctrlId, fn3) END_MAP where each function returns true if the event was handled... Since in D, there are no such macro and such tables are common and usefull, it might be interesting to extend switch to be able to do something similar: switch (event) { case is_cmd(id, event) : break if fn1(); case is_exit(event) : break if fn2(event); }
Sep 18 2003
prev sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
I just thought of another thing:

If we allow function calls in the case value we cannot prevent side 
effects that mess with the intended semantics. E.g.

class X
{
	bool stupid=false;

	bool isCool()
	{
		stupid=true;
		return false;
	}

	bool isStupid()
	{
		return stupid;
	}
}

X x;

switch(x as sval)
{
case sval.isCool():	someFunc();
	break;
case sval.isStupid():	someOtherFunc();
	break;
}

The outcome of this switch statement depends on the order the case 
statements are evaluated. Of course, that is up to the compiler, so 
things could work with one compiler version but might not with another.

The following code snippet has the same problem:
if(x.isCool() || x.isStupid())
as does
someFunc(x.isCool(),x.isStupid())

Does D define an evaluation order for conditional statements and/or 
function arguments? I think it would be important to have the switch 
statement behave in a way that is consistent to that.


Hauke
Sep 17 2003
parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
The example below is hypothetical, I believe.

I believe that nothing that is too difficult to enforce should be
enforced by the language.

If a programmer really does what he does in isCool(), he gets all he
deserves...

The issue here is "side-effects inside functions that are supposed to be
side-effectless". There's not much point trying to do that, and it's
really difficult to do it by mistake.

A good design principle I've bounced into:

"Protect the programmer from Murphy, not Macchiavelli"

I believe I first heard it in context of C++, but I can't find the
original source here. google finds an explanation at:

http://photon.poly.edu/~hbr/cs903-F00/lib_design/notes/advanced.html

"We can distinguish between to kinds of protection a design can provide:
protection against Murphy, and protection against Macchiavelly. Murphy
describes an user that makes occasionally mistakes, while Macchiavelli
describes an user that willingly tries to get around the protection
mechanism. Protection against Macchiavelli in C++ is almost impossible,
as C-style type casts and the example above illustrates. An effective
but expensive solution would be opaque pointers and a link library
manipulating the opaque pointer, where the sources of the underlying
data are not published. Here, we discuss usually solutions that protect
against Murphy."

Paul Graham goes even further and claims that programming languages
should be designed for the smart people. http://www.paulgraham.com, lots
of articles, "Five Questions about Language Design" might not be a bad
place to start with.

-Antti

In article <bk97sm$1e9m$1 digitaldaemon.com>, Hauke Duden wrote:
 I just thought of another thing:
 
 If we allow function calls in the case value we cannot prevent side 
 effects that mess with the intended semantics. E.g.
 
 class X
 {
 	bool stupid=false;
 
 	bool isCool()
 	{
 		stupid=true;
 		return false;
 	}
 
 	bool isStupid()
 	{
 		return stupid;
 	}
 }
 
 X x;
 
 switch(x as sval)
 {
 case sval.isCool():	someFunc();
 	break;
 case sval.isStupid():	someOtherFunc();
 	break;
 }
 
 The outcome of this switch statement depends on the order the case 
 statements are evaluated. Of course, that is up to the compiler, so 
 things could work with one compiler version but might not with another.
 
 The following code snippet has the same problem:
 if(x.isCool() || x.isStupid())
 as does
 someFunc(x.isCool(),x.isStupid())
 
 Does D define an evaluation order for conditional statements and/or 
 function arguments? I think it would be important to have the switch 
 statement behave in a way that is consistent to that.
 
 
 Hauke
 

Sep 17 2003
parent Hauke Duden <H.NS.Duden gmx.net> writes:
Antti Sykäri wrote:
 The issue here is "side-effects inside functions that are supposed to be
 side-effectless". There's not much point trying to do that, and it's
 really difficult to do it by mistake.
 
 A good design principle I've bounced into:
 
 "Protect the programmer from Murphy, not Macchiavelli"

Actually, my main point was to keep it consistent with the rest of the language. Bad programmers will always find a way to mess up, but I'd at least like the language to behave the same in regard to side effects in all contexts. Hauke
Sep 18 2003
prev sibling next sibling parent "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Antti Sykäri" <jsykari gamma.hut.fi> ha scritto nel messaggio
news:slrnbmdvgs.jlv.jsykari pulu.hut.fi...
 The compiler will in this case have to recognize the "old-style" switch
 statements (where all cases are constant and maybe not overlapping),
 which should be trivial, and perform the usual optimizations on them.
 More complex statements (where some case is non-constant) are
 implemented as an if-elseif-etc-else statement.

Great! Now I'm curious about Walter's opinion, he's the compiler guy after all... Ric
Sep 16 2003
prev sibling parent reply John Boucher <John_member pathlink.com> writes:
Oh, so we're going to start specifying a new syntax for the switch statement?
Cool!

Because I recently decided that I would always put braces around the statements
in cases (to more obviously group them visually) (I also always use braces with
ifs, fors, and whiles -- more coherent visual presentation) I propose that they
be required, so howsabout:

switch ( expr )
{
case ( 1 )
{
..
break;
}

case ( 2 , 3 )
{
..
nobreak;
}

default
{
..
break;
}
}

An additional benefit of this syntax is that it removes the colon, thereby
freeing it up for use in the assignment operator(s).

John Boucher -- Quite contrary
The King had Humpty pushed.
Sep 16 2003
parent "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"John Boucher" <John_member pathlink.com> ha scritto nel messaggio
news:bk7e08$6u2$1 digitaldaemon.com...
 Because I recently decided that I would always put braces around the

 in cases (to more obviously group them visually) (I also always use braces

 ifs, fors, and whiles -- more coherent visual presentation) I propose that

 be required, so howsabout:

 switch ( expr )
 {
 case ( 1 )
 {
 ..
 break;
 }

 case ( 2 , 3 )
 {
 ..
 nobreak;
 }

 default
 {
 ..
 break;
 }
 }

 An additional benefit of this syntax is that it removes the colon, thereby
 freeing it up for use in the assignment operator(s).

Besides, this way each case is a scope of its own and may have local variables. I often use braces this way in switch statements and surely wouldn't mind them being required. Ric
Sep 18 2003
prev sibling parent "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Hauke Duden" <H.NS.Duden gmx.net> ha scritto nel messaggio
news:bk6n5l$11j6$1 digitaldaemon.com...
 Is the last 'case' consistent with the proposed use of the "is" operator
 as a replacement for ===? Wouldn't this mean that if x is an object,
 then the pointer would be compared with 1?

Nice and right objection. I blind-copied it from VB. "is" might as well be omitted altogether.
 One thing to consider is that case statements involving objects and
 overloaded operators make it impossible for the compiler to check wether
 they are non-overlapping.

I think the first matching case should apply.
 Now that I think more about it, I think retrofitting switch to become as
 powerful as if is probably not a good idea at all. Lets face it, the
 switch statement is really only an optimization to help the compiler
 generate efficient code. If switch was as powerful as if, then there
 would be no additional optimization possibilities and the whole
 statement would be pretty useless. [Oh the irony ;)].

The switch statement is a logical construct; the fact that it *can* be optimized, e.g. using jump tables, pertains to the compiler's internals. I don't think that such extreme optimizations are needed any longer on today's machines, or at least they could be sacrificed (at least partially; DMD _is_ an optimizing compiler, it does not optimize only switch statements...) if the advantages in code readability and maintainability are found to be more important than the loss in pure speed. Besides, when the switch expression is really an expression (as opposed to a single variable) it is computed once, which doesn't happen with chained if's... Ric
Sep 16 2003
prev sibling next sibling parent reply Erik Baklund <Erik_member pathlink.com> writes:
Please not that the suggested syntax leaves both 'break' and 'nobreak'
redundant.

erik

In article <bk6fbt$8n2$1 digitaldaemon.com>, Riccardo De Agostini says...
"Ian Woollard" <Ian_member pathlink.com> ha scritto nel messaggio
news:bk5nri$14cm$1 digitaldaemon.com...
 [...] The default behaviour is really unsafe. I would
 suggest a 'nobreak' statement for cases where you really do want the code

 fall through; otherwise  the compiler probably should fail to compile

 switcheable off with a compiler option.)

I vote for that too. And BTW, Walter, while we're on the subject... how about multiple-value case labels, a la VB? switch (x) { case 0: ... case 1, 3, 5: ... case 2, 4, 6 to 10: // or 6..10 as in Pascal, or [6, 10] or whatever ... case is < 1: // see comments on this one below ... case 11, 13 to 15, is > 16: // All together now! ... default: // no, I'm not proposing a "case else" :) } While all other forms are simply short for writing more labels, the "is" form really requires the switch to be compiled as it was a series of if's, with the difference that part of the if expression has already been evaluated. The syntax would be "is <relational operator> <expression>" If the switched-on expression is an object, overloaded operators might be used (provided it's not already so); in this case, "case is <obj_reference_or_null>" would compare pointers instead of checking object equality. Note that I'm following the "is" operator proposal here... OK, just daydreaming maybe... I suppose that optimizing such a thing would be rather hard... anyway I tried... Ric

Sep 16 2003
parent reply "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Erik Baklund" <Erik_member pathlink.com> ha scritto nel messaggio
news:bk6vgp$1rn2$1 digitaldaemon.com...
 Please not that the suggested syntax leaves both 'break' and 'nobreak'
 redundant.

Of course: break should be implied. Thanks for pointing that out. Ric
Sep 16 2003
parent reply Erik Baklund <Erik_member pathlink.com> writes:
I am sorry that my statement came out a bit garbeled.
My intension was to say that I appreciated your syntax because it suggested the
possibility of a cleaner implementation of the 'switch' statement.
It even leaves both keywords 'break' and 'nobreak' redundant.
The reason is that the semantics of both keywords could be interpreted to be
implicit in the syntax:

switch(n)
{
case 4 [..mplicit nobreak..], 5: 
{
// Some code
}
[..implicit break..]

default:
{
// Some code
}
}

The benefit I saw was was that :
1) A new keyword 'nobreak' is not needed. Fallback (nobreak) through null-code
is allowed, when cases are stated on the same line.
2) The old keyword 'break' is not needed because it could be decided always to
be be implicit. Thus, it would not any longer be possible to create a bug by
forgetting 'break' because there is no 'break' to forget.
3) Clean value/code-block mapping is enforced. i.e. implicit breaks would
prevent programmers from fragmenting code blocks into conditional parts and
spread them over several 'case'es (a bad practice that is possible in C/C++). 

erik
In article <bk714l$20ms$2 digitaldaemon.com>, Riccardo De Agostini says...
"Erik Baklund" <Erik_member pathlink.com> ha scritto nel messaggio
news:bk6vgp$1rn2$1 digitaldaemon.com...
 Please not that the suggested syntax leaves both 'break' and 'nobreak'
 redundant.

Of course: break should be implied. Thanks for pointing that out. Ric

Sep 16 2003
parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 I am sorry that my statement came out a bit garbeled.
 My intension was to say that I appreciated your syntax because it

 possibility of a cleaner implementation of the 'switch' statement.
 It even leaves both keywords 'break' and 'nobreak' redundant.
 The reason is that the semantics of both keywords could be interpreted to

 implicit in the syntax:

 switch(n)
 {
 case 4 [..mplicit nobreak..], 5:
 {
 // Some code
 }
 [..implicit break..]

 default:
 {
 // Some code
 }
 }

 The benefit I saw was was that :
 1) A new keyword 'nobreak' is not needed. Fallback (nobreak) through

 is allowed, when cases are stated on the same line.
 2) The old keyword 'break' is not needed because it could be decided

 be be implicit. Thus, it would not any longer be possible to create a bug

 forgetting 'break' because there is no 'break' to forget.
 3) Clean value/code-block mapping is enforced. i.e. implicit breaks would
 prevent programmers from fragmenting code blocks into conditional parts

 spread them over several 'case'es (a bad practice that is possible in

 erik

And how do you handle fall-through? With a goto (maybe something like goto case(4) where one would give the expression that indicate the target; if this expression is constant, the compiler can make a direct jump; otherwise we switch on that new value). This would even be more powerfull that any other solution I have seen presented: int some_value = ...; switch (some_expr()) { case 1 : goto case(some_value); // redo the switch with that new value. case 2: goto case(1); // compile-time jump to case 1 case 3 : do_something(); goto case(default); // default is a special value for jumping to default default: do_something_else(); }
Sep 16 2003
next sibling parent John Boucher <John_member pathlink.com> writes:
In article <bk823t$2cpv$1 digitaldaemon.com>, Philippe Mori says...
 I am sorry that my statement came out a bit garbeled.
 My intension was to say that I appreciated your syntax because it

 possibility of a cleaner implementation of the 'switch' statement.
 It even leaves both keywords 'break' and 'nobreak' redundant.
 The reason is that the semantics of both keywords could be interpreted to

 implicit in the syntax:

 switch(n)
 {
 case 4 [..mplicit nobreak..], 5:
 {
 // Some code
 }
 [..implicit break..]

 default:
 {
 // Some code
 }
 }

 The benefit I saw was was that :
 1) A new keyword 'nobreak' is not needed. Fallback (nobreak) through

 is allowed, when cases are stated on the same line.
 2) The old keyword 'break' is not needed because it could be decided

 be be implicit. Thus, it would not any longer be possible to create a bug

 forgetting 'break' because there is no 'break' to forget.
 3) Clean value/code-block mapping is enforced. i.e. implicit breaks would
 prevent programmers from fragmenting code blocks into conditional parts

 spread them over several 'case'es (a bad practice that is possible in

 erik

And how do you handle fall-through? With a goto (maybe something like goto case(4) where one would give the expression that indicate the target; if this expression is constant, the compiler can make a direct jump; otherwise we switch on that new value). This would even be more powerfull that any other solution I have seen presented: int some_value = ...; switch (some_expr()) { case 1 : goto case(some_value); // redo the switch with that new value. case 2: goto case(1); // compile-time jump to case 1 case 3 : do_something(); goto case(default); // default is a special value for jumping to default default: do_something_else(); }

Oh, yeah, specific jumping rather than falling-through _is_ better. Let's get rid of fall-through altogether... in the next language maybe. Boy, I'd better start keeping a list. John Boucher -- Maybe not so contrary after all The King had Humpty pushed
Sep 16 2003
prev sibling parent reply Erik Baklund <Erik_member pathlink.com> writes:
Nonono .. no goto!

This thread discusses two topics in paralell.
One discussion is about "how to prevent codemakers doing mistakes with
'switch'", the other one is "what can we add of neat features to the 'switch'
statement".
Since I am supervizing programmers and seing real project money being 
spent on fixing mistakes associated with 'switch', my comments
was about how make the 'switch' statement safer.

There are two common mistakes with 'switch' that I wanted to prevent:
1) Forgetting to add 'break'.
2) Excersize the bad practise of implementing conditional excecution paths using
'switch'. The latter practise is often less successful and should be done by
regular 'if' statements.

To me the 'switch' statement is a state to code-block mapping tool.
The state being codified in the 'switch' argument.
Compiled languages most often constrains the switch argument
to be an integer so that hardware supported fast lookup can be leveraged.

With that perspective in mind, the only time one should wish to
leave out the 'break', is when several states are mapping to the same
code-block. In other words, 'fall-through' should only be permitted through
empty code blocks. Your syntax "case 2, 3, 5:" expressed this so elegantly, and
suddenly 'nobreak' and 'break' was needed no more.

Kind regards,
Erik



In article <bk823t$2cpv$1 digitaldaemon.com>, Philippe Mori says...
 I am sorry that my statement came out a bit garbeled.
 My intension was to say that I appreciated your syntax because it

 possibility of a cleaner implementation of the 'switch' statement.
 It even leaves both keywords 'break' and 'nobreak' redundant.
 The reason is that the semantics of both keywords could be interpreted to

 implicit in the syntax:

 switch(n)
 {
 case 4 [..mplicit nobreak..], 5:
 {
 // Some code
 }
 [..implicit break..]

 default:
 {
 // Some code
 }
 }

 The benefit I saw was was that :
 1) A new keyword 'nobreak' is not needed. Fallback (nobreak) through

 is allowed, when cases are stated on the same line.
 2) The old keyword 'break' is not needed because it could be decided

 be be implicit. Thus, it would not any longer be possible to create a bug

 forgetting 'break' because there is no 'break' to forget.
 3) Clean value/code-block mapping is enforced. i.e. implicit breaks would
 prevent programmers from fragmenting code blocks into conditional parts

 spread them over several 'case'es (a bad practice that is possible in

 erik

And how do you handle fall-through? With a goto (maybe something like goto case(4) where one would give the expression that indicate the target; if this expression is constant, the compiler can make a direct jump; otherwise we switch on that new value). This would even be more powerfull that any other solution I have seen presented: int some_value = ...; switch (some_expr()) { case 1 : goto case(some_value); // redo the switch with that new value. case 2: goto case(1); // compile-time jump to case 1 case 3 : do_something(); goto case(default); // default is a special value for jumping to default default: do_something_else(); }

Sep 17 2003
parent reply John Boucher <John_member pathlink.com> writes:
In article <bk9vjp$2fel$1 digitaldaemon.com>, Erik Baklund says...
Nonono .. no goto!

There are two common mistakes with 'switch' that I wanted to prevent:
1) Forgetting to add 'break'.
2) Excersize the bad practise of implementing conditional excecution paths using
'switch'. The latter practise is often less successful and should be done by
regular 'if' statements.

Kind regards,
Erik

I think I agree. I definitely agree with your first assertion, that there should be no default fall-through. And I also like the idea of a case having a list of values. It seems to me that the switch statement was created to make the coding of an ugly series of _simple_ ifs easier to read (and providing a performance boost as well). So I'm less enthusistic about trying to make it handle an ugly series of _tricky_ ifs. (On the other hand, it seems that allowing ranges would allow the use of reals in the switch statement.) However, given that we are used to fall-through, it seems like there should be a way to allow that. Adding a nobreak or something will provide that. Buuut... another problem with fall-through is what happens when a new case is added in the middle of a switch and gets fallen-through to accidently -- another rookie mistake. That's why _I_ now think that a goto of some sort to _specify_ which case to execute next is a good thing. And I think that it would need to be a construct that will only allow gotoing to a lexically later case. (And, yes, my opinion has changed from a day or two ago.) I'm just not sure what to suggest for the gotoing syntax, how about: nextcase ( 1 ) ; // Or whatever Upon further reflection, I also think that such a statement would only be allowed once in a case, as the last line of course. If something trickier is required, don't use a switch. Having said that, mightn't it still be legal to use a regular goto to invalidate the whole switch concept anyway? As in... switch ( expr ) { case 2 : .. gobackhere: // Naughty naughty // Could be an endless loop too! .. case 1 : .. goto gobackhere ; } So, yes, rework the switch statement to reduce errors and improve clarity, but _not_ to add functionality. John Boucher The King had Humpty pushed.
Sep 17 2003
parent "Philippe Mori" <philippe_mori hotmail.com> writes:
 And I also like the idea of a case having a list of values.

Me too...
 However, given that we are used to fall-through, it seems like there

 way to allow that. Adding a nobreak or something will provide that.

 another problem with fall-through is what happens when a new case is added

 the middle of a switch and gets fallen-through to accidently -- another

 mistake.

 That's why _I_ now think that a goto of some sort to _specify_ which case

 execute next is a good thing. And I think that it would need to be a

 that will only allow gotoing to a lexically later case. (And, yes, my

 has changed from a day or two ago.)

 I'm just not sure what to suggest for the gotoing syntax, how about:
 nextcase ( 1 ) ;  // Or whatever
 Upon further reflection, I also think that such a statement would only be
 allowed once in a case, as the last line of course. If something trickier

 required, don't use a switch.

No it should be allowed anywhere as it is allowed to return from anywhere inside a function... It should be possible to contionally go to another case.
 Having said that, mightn't it still be legal to use a regular goto to

 the whole switch concept anyway? As in...
 switch ( expr )
 {
 case 2 :
 ..
 gobackhere:                     // Naughty naughty
 // Could be an endless loop too!
 ..

 case 1 :
 ..
 goto gobackhere ;
 }

 So, yes, rework the switch statement to reduce errors and improve clarity,

 _not_ to add functionality.

So, in resume, what we would like is breaking by default and allowing branching to a case by value... Otherwise, same as current switch (for ex. we are still able to uses goto if we really want).
Sep 18 2003
prev sibling next sibling parent "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Riccardo De Agostini" <riccardo.de.agostini email.it> ha scritto nel
messaggio news:bk6fbt$8n2$1 digitaldaemon.com...
 I vote for that too.

Except for the compiler option, I meant. Let D be D. Ric
Sep 16 2003
prev sibling parent reply "Philippe Mori" <philippe_mori hotmail.com> writes:
 And BTW, Walter, while we're on the subject... how about multiple-value

 labels, a la VB?

 switch (x)
 {
 case 0:
     ...
 case 1, 3, 5:
     ...
 case 2, 4, 6 to 10:  // or 6..10 as in Pascal, or [6, 10] or whatever
     ...
 case is < 1:  // see comments on this one below
     ...
 case 11, 13 to 15, is > 16:  // All together now!
     ...
 default:  // no, I'm not proposing a "case else" :)
 }

For me, multiple values is OK and range also (with either syntax) But for the is operator, I'm not sure... I think those case could be handled if the default case with regular if... OTOH, this might be a good solution particulary if we add the nodefault (see below). IMO, is is surperfluous. We could have < 1 or .. 1 or [ , 10] but maybe it should be required just to ensure that there are no unintended errors... I think that each range should be mutually exclusive so that it is illegal to have the same value twice once explicit and once in a range. We should have also an option to specify that each possible values must have a label for case when all cases must be handled. This would typically be usefull for enumerations where we might want to ensure that all values appears. switch (enumVar) { case e1 : break; case e2 : doSomething(); nobreak; case e3 : doSomethingElse(); break; nodefault: // All valid value for the type must appears in the switch } This would be great for cases where an enum is needed and we must handle all case...
Sep 16 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Philippe Mori" <philippe_mori hotmail.com> wrote in message
news:bk7gft$gju$1 digitaldaemon.com...
 We should have also an option to specify that each possible
 values must have a label for case when all cases must be
 handled. This would typically be usefull for enumerations
 where we might want to ensure that all values appears.

A good idea, but this is already addressed in D where the default case, if not specified, is to throw an exception.
Nov 26 2003
next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Walter wrote:
We should have also an option to specify that each possible
values must have a label for case when all cases must be
handled. This would typically be usefull for enumerations
where we might want to ensure that all values appears.

A good idea, but this is already addressed in D where the default case, if not specified, is to throw an exception.

Not that I'm against that, but it seems a little inconsistent. What happened to that "we cannot change the semantics of switch because that would confuse C/C++ coders" rule? Throwing an exception where C/C++ would just continue is a rather big difference, isn't it? If that rule has been dropped - how about adding an implicit break as well? ;) Hauke
Nov 26 2003
parent "Charles Sanders" <sanders-consulting comcast.net> writes:
 If that rule has been dropped - how about adding an implicit break as
 well? ;)

Seconded! C "Hauke Duden" <H.NS.Duden gmx.net> wrote in message news:bq2t8k$qe1$1 digitaldaemon.com...
 Walter wrote:
We should have also an option to specify that each possible
values must have a label for case when all cases must be
handled. This would typically be usefull for enumerations
where we might want to ensure that all values appears.

A good idea, but this is already addressed in D where the default case,


 not specified, is to throw an exception.

Not that I'm against that, but it seems a little inconsistent. What happened to that "we cannot change the semantics of switch because that would confuse C/C++ coders" rule? Throwing an exception where C/C++ would just continue is a rather big difference, isn't it? If that rule has been dropped - how about adding an implicit break as well? ;) Hauke

Nov 26 2003
prev sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
 We should have also an option to specify that each possible
 values must have a label for case when all cases must be
 handled. This would typically be usefull for enumerations
 where we might want to ensure that all values appears.

A good idea, but this is already addressed in D where the default case, if not specified, is to throw an exception.

Wow! That's a *really*, *REALLY* bad idea. If one is porting some code which very rarely uses the default case - it's largely irrelevant whether that's benign or a bug - it may easily escape the test cases, and "sometime later", in production code, an exception will be thrown which has never been anticipated and therefore never been handled. This is about the very best/worst example I could possibly think of in the debate between compile-time and runtime error-handling. Rather than causing me 30 seconds in development time, I am up for an unknown, and potentially huge, cost sometime after deployment. Is D really wanting to be a serious systems language? Yours, in a state of stupefaction Matthew
Nov 26 2003
next sibling parent reply Ant <Ant_member pathlink.com> writes:
In article <bq2vi2$tqd$1 digitaldaemon.com>, Matthew Wilson says...
 We should have also an option to specify that each possible
 values must have a label for case when all cases must be
 handled. This would typically be usefull for enumerations
 where we might want to ensure that all values appears.

A good idea, but this is already addressed in D where the default case, if not specified, is to throw an exception.

Wow! That's a *really*, *REALLY* bad idea.

I agree! Why nobody else is complaining? was this discussed before? Since the first time I was surprised with the exception I just automatically type in an empty default. Ant
Nov 26 2003
next sibling parent "Charles Sanders" <sanders-consulting comcast.net> writes:
I havent come accross it, but mark this up as another complaint ! :).

C


"Ant" <Ant_member pathlink.com> wrote in message
news:bq33o3$140q$1 digitaldaemon.com...
 In article <bq2vi2$tqd$1 digitaldaemon.com>, Matthew Wilson says...
 We should have also an option to specify that each possible
 values must have a label for case when all cases must be
 handled. This would typically be usefull for enumerations
 where we might want to ensure that all values appears.

A good idea, but this is already addressed in D where the default case,



 not specified, is to throw an exception.

Wow! That's a *really*, *REALLY* bad idea.

I agree! Why nobody else is complaining? was this discussed before? Since the first time I was surprised with the exception I just automatically type in an empty default. Ant

Nov 26 2003
prev sibling parent reply "Vathix" <vathix dprogramming.com> writes:
"Ant" <Ant_member pathlink.com> wrote in message
news:bq33o3$140q$1 digitaldaemon.com...
 In article <bq2vi2$tqd$1 digitaldaemon.com>, Matthew Wilson says...
 We should have also an option to specify that each possible
 values must have a label for case when all cases must be
 handled. This would typically be usefull for enumerations
 where we might want to ensure that all values appears.

A good idea, but this is already addressed in D where the default case,



 not specified, is to throw an exception.

Wow! That's a *really*, *REALLY* bad idea.

I agree! Why nobody else is complaining? was this discussed before? Since the first time I was surprised with the exception I just automatically type in an empty default. Ant

I do that too, and don't like it much. I do, however, like how an exception is thrown if the end of a non-void function is reached. By the way, I like how switch cases fall through silently; probably for the same reasons as Walter. I proposed that we use a new keyword for cases that automatically break when a new case is found, such as: switch(s) { case 3: falls through bcase 4: auto-breaks at next case, bcase, or default }
Nov 26 2003
parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Vathix wrote:
 I do that too, and don't like it much. I do, however, like how an exception
 is thrown if the end of a non-void function is reached.

Huh? How can that happen? Doesn't the compiler check at compile time that there is a return statement in each possible path? All C/C++ compilers I know will at least issue a warning in such a case (though it should be an error). DMD doesn't do that? Hauke
Nov 26 2003
next sibling parent Juan C. <Juan_member pathlink.com> writes:
As regards the switch statement, I continue to like the way C# does it; you must
either _break_ or _goto_ another case, no fall through, zip, nada, zilch. And
the compiler tells you when you try.

<stump>
I would also prefer it be required that the statements in a _case_ be placed in
a block surrounded by braces _{}_ to better group them visually. Same with _if_,
_while_, _for_, _do_ ... anywhere where it is currently required *sometimes*, it
should be required *all the time*. Easier reading, I expect easier
parsing/lexing whatever.

It shouldn't be too difficult to write a script to go through the C code and add
the otherwise extraneous stuff (doesn't hurt the C any) before attempting to
port it.
</stump>
Nov 26 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
news:bq3blg$1fvl$1 digitaldaemon.com...
 Vathix wrote:
 I do that too, and don't like it much. I do, however, like how an


 is thrown if the end of a non-void function is reached.

Huh? How can that happen? Doesn't the compiler check at compile time that there is a return statement in each possible path? All C/C++ compilers I know will at least issue a warning in such a case (though it should be an error). DMD doesn't do that?

The problem is it's not possible to do this 100%. Consider the trivial case: int foo(int x) { while (x) { blahblah if (blah blah) return 3; } } The programmer may know that x is never 0, but the compiler can't figure it out. Hence it stuffs a throw at the end. Sure, it's a contrived example, but I've seen cases of this in real code.
Nov 26 2003
next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Walter wrote:
 "Hauke Duden" <H.NS.Duden gmx.net> wrote in message
 news:bq3blg$1fvl$1 digitaldaemon.com...
 
Vathix wrote:

I do that too, and don't like it much. I do, however, like how an



 The problem is it's not possible to do this 100%. Consider the trivial case:
 
     int foo(int x)
     {
         while (x)
         {
             blahblah
             if (blah blah)
                 return 3;
         }
     }
 
 The programmer may know that x is never 0, but the compiler can't figure it
 out. Hence it stuffs a throw at the end. Sure, it's a contrived example, but
 I've seen cases of this in real code.

VC6 issues a warning in that case: "warning C4715: 'foo' : not all control paths return a value" I agree that such a case should be allowed without requiring an explicit return that you know will never be reached. But can't the compiler issue a warning at compile time in addition to the runtime exception. I quite often write functions that return a boolean that (should) end with "return true;". However, I sometimes simply forget the last return (having just dealt with complicated error-cases the normal case return sometimes slips my mind). I like it if the compiler warns me about that. Maybe that warning could be made optional with a commandline switch? Hauke
Nov 27 2003
next sibling parent reply Mark T <Mark_member pathlink.com> writes:
 
     int foo(int x)
     {
         while (x)
         {
             blahblah
             if (blah blah)
                 return 3;
         }
     }
 
 The programmer may know that x is never 0, but the compiler can't figure it
 out. Hence it stuffs a throw at the end. Sure, it's a contrived example, but
 I've seen cases of this in real code.

VC6 issues a warning in that case: "warning C4715: 'foo' : not all control paths return a value" I agree that such a case should be allowed without requiring an explicit return that you know will never be reached.

why allow it? this is bad code, at least issue a warning because someone someday will call foo() with a 0
Nov 27 2003
next sibling parent Hauke Duden <H.NS.Duden gmx.net> writes:
Mark T wrote:
    int foo(int x)
    {
        while (x)
        {
            blahblah
            if (blah blah)
                return 3;
        }
    }

The programmer may know that x is never 0, but the compiler can't figure it
out. Hence it stuffs a throw at the end. Sure, it's a contrived example, but
I've seen cases of this in real code.

VC6 issues a warning in that case: "warning C4715: 'foo' : not all control paths return a value" I agree that such a case should be allowed without requiring an explicit return that you know will never be reached.

why allow it? this is bad code, at least issue a warning because someone someday will call foo() with a 0

Well, if 0 is an invalid argument, then throwing an exception would be the right thing to do. This might save the programmer the need to do it manually. Though now that I think about it ... in almost all cases I can imagine one would want the exception to be something like InvalidArgError. If you get UnexpectedEndOfFunction (or whatever is thrown) that sounds like there is a bug in the function, instead of a bug in the calling code. And these cases should be rare enough that an "unnecessary" return statement wouldn't really amount to much of a hassle. I also just realized that we will never get Walter to add warnings to the compiler, so I'll switch sides. I'm all for making it an error now ;). It's still much better than having the compiler not complain at all about such a thing... Hauke
Nov 27 2003
prev sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
     int foo(int x)
     {
         while (x)
         {
             blahblah
             if (blah blah)
                 return 3;
         }
     }

 The programmer may know that x is never 0, but the compiler can't



 out. Hence it stuffs a throw at the end. Sure, it's a contrived



 I've seen cases of this in real code.

VC6 issues a warning in that case: "warning C4715: 'foo' : not all control paths return a value" I agree that such a case should be allowed without requiring an explicit return that you know will never be reached.

why allow it? this is bad code, at least issue a warning because someone

 will call foo() with a 0

Agreed. It has to be an error.
Nov 27 2003
parent Felix <Felix_member pathlink.com> writes:
Me too... I think that syntax should be more strict in D than in C/C++, so I
agree with Walter's PoW: a piece of code is either acceptable or not i.e. gives
error or not (ok, viceversa ;). For this case I think the return should be
explicitely given or, at least, to state that any function will return
(implicitely) the initialization type value (the variable's default value).
Anyway, interesting subject.

BTW in Matlab, the warning is like: Warning: some output arguments never
assigned in function xxx.



In article <bq5lir$1v0j$1 digitaldaemon.com>, Matthew Wilson says...
     int foo(int x)
     {
         while (x)
         {
             blahblah
             if (blah blah)
                 return 3;
         }
     }

 The programmer may know that x is never 0, but the compiler can't



 out. Hence it stuffs a throw at the end. Sure, it's a contrived



 I've seen cases of this in real code.

VC6 issues a warning in that case: "warning C4715: 'foo' : not all control paths return a value" I agree that such a case should be allowed without requiring an explicit return that you know will never be reached.

why allow it? this is bad code, at least issue a warning because someone

 will call foo() with a 0

Agreed. It has to be an error.

Nov 28 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
 Maybe that warning could be made optional with a commandline switch?

I need to write a short paper on why I philosophically object to warnings! (This issue comes up repeatedly.)
Nov 27 2003
next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Walter wrote:
 "Hauke Duden" <H.NS.Duden gmx.net> wrote in message
 
Maybe that warning could be made optional with a commandline switch?

I need to write a short paper on why I philosophically object to warnings! (This issue comes up repeatedly.)

I know the way you feel about this. Maybe you're right and the necessity for warnings are a sign of a badly designed language. However - and I don't mean to be trolling here, just speaking my mind - if you refuse to employ one particular kind of workaround for a design flaw then you shouldn't just invent a new workaround, but instead try to find a real solution for the flaw! This kind of exception is just a kind of warning (actually more like an error) that is issued at runtime, instead of compile time. IMHO that's a step in the wrong direction. Hauke
Nov 27 2003
parent "Walter" <walter digitalmars.com> writes:
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
news:bq60t4$2gol$1 digitaldaemon.com...
 This kind of exception is just a kind of warning (actually more like an
 error) that is issued at runtime, instead of compile time. IMHO that's a
 step in the wrong direction.

You're right that it would be better to do it at compile time, but like array bounds checking, it is impossible to do at compile time.
Nov 27 2003
prev sibling parent reply Roberto Mariottini <Roberto_member pathlink.com> writes:
In article <bq5g2i$1m63$1 digitaldaemon.com>, Walter says...
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
 Maybe that warning could be made optional with a commandline switch?

I need to write a short paper on why I philosophically object to warnings! (This issue comes up repeatedly.)

You forget you philosophically object to compiler options, too. I suggest to add it to your paper. Ciao
Nov 28 2003
parent "Walter" <walter digitalmars.com> writes:
"Roberto Mariottini" <Roberto_member pathlink.com> wrote in message
news:bq7g8g$1n6g$1 digitaldaemon.com...
 In article <bq5g2i$1m63$1 digitaldaemon.com>, Walter says...
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
 Maybe that warning could be made optional with a commandline switch?

I need to write a short paper on why I philosophically object to


(This issue comes up repeatedly.)

You forget you philosophically object to compiler options, too. I suggest to add it to your paper.

LOL! They're both related, that's for sure.
Nov 28 2003
prev sibling next sibling parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bq3pkp$24v0$1 digitaldaemon.com...
 Huh? How can that happen? Doesn't the compiler check at compile time
 that there is a return statement in each possible path? All C/C++
 compilers I know will at least issue a warning in such a case (though it
 should be an error). DMD doesn't do that?

The problem is it's not possible to do this 100%. Consider the trivial

     int foo(int x)
     {
         while (x)
         {
             blahblah
             if (blah blah)
                 return 3;
         }
     }

 The programmer may know that x is never 0, but the compiler can't figure

 out. Hence it stuffs a throw at the end. Sure, it's a contrived example,

 I've seen cases of this in real code.

It is conceivable that due to a HW glitch or Act of God that x actually turns up 0 once every 4 billion runs. If you can't be 200% sure, I would rather the compiler bitch at me, or automatically insert some code that will never run, bloating my app by a few bytes, than take the chance of having execution fall off the end of my function. It *is* possible to do this 100% if you require that all return paths are explicitly marked and handled, i.e. reject the above code as erroneous due to potential execution path problems. The problem with the above function is that it's probably written inside out anyway... maybe it should have been written as this: int foo(int x) { while (x) { blahblah if (blah blah) break; } return 3; } or this: int foo(int x) { if (x) do { blahblah }while (!blah blah); return 3; } Sean
Nov 28 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <palmer.sean verizon.net> wrote in message
news:bq7boo$1g93$1 digitaldaemon.com...
 It is conceivable that due to a HW glitch or Act of God that x actually
 turns up 0 once every 4 billion runs.

I think that protecting against CPU hardware failures is beyond the scope of the language.
 If you can't be 200% sure, I would
 rather the compiler bitch at me, or automatically insert some code that

 never run, bloating my app by a few bytes, than take the chance of having
 execution fall off the end of my function.

It does the latter now <g>.
 It *is* possible to do this 100% if you require that all return paths are
 explicitly marked and handled, i.e. reject the above code as erroneous due
 to potential execution path problems.

Several C++ compilers do emit warnings for such. I find those warnings to be irritating rather than useful. For example, they will warn about: int foo() { while (1) { .... return ...; } } where it is *not possible* for the code to fall off the end, but the warning happens, and to get rid of it I have to put in a return statement that will never execute. I find being forced to add code that will never execute to be inelegant and potentially confusing. Hence my motivation for having D ensure that it never executes by raising an exception if it does, but that doesn't muck up the source code.
Nov 28 2003
next sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bq897h$2s6v$1 digitaldaemon.com...
 "Sean L. Palmer" <palmer.sean verizon.net> wrote in message
 news:bq7boo$1g93$1 digitaldaemon.com...
 It is conceivable that due to a HW glitch or Act of God that x actually
 turns up 0 once every 4 billion runs.

I think that protecting against CPU hardware failures is beyond the scope

 the language.

 If you can't be 200% sure, I would
 rather the compiler bitch at me, or automatically insert some code that

 never run, bloating my app by a few bytes, than take the chance of


 execution fall off the end of my function.

It does the latter now <g>.
 It *is* possible to do this 100% if you require that all return paths


 explicitly marked and handled, i.e. reject the above code as erroneous


 to potential execution path problems.

Several C++ compilers do emit warnings for such. I find those warnings to

 irritating rather than useful. For example, they will warn about:

     int foo()
     {
         while (1)
         {
                 ....
                     return ...;
         }
     }

 where it is *not possible* for the code to fall off the end, but the

 happens, and to get rid of it I have to put in a return statement that

 never execute. I find being forced to add code that will never execute to

 inelegant and potentially confusing. Hence my motivation for having D

 that it never executes by raising an exception if it does, but that

 muck up the source code.

So your argument is that because some code analysers in some compilers are less than perfect, you'll change the language itself, and push out error detection from fool-proof and efficient compile-time to unexpected, surprising and inefficient runtime. I am running out of adjectives on this one. Suffice to say, this is going to be really good ammunition for D-etractors.
Nov 28 2003
next sibling parent reply Ilya Minkov <minkov cs.tum.edu> writes:
Matthew Wilson wrote:

 I am running out of adjectives on this one. Suffice to say, this is going to
 be really good ammunition for D-etractors.

D-ctatorship rules. :> Walter may be right, but when using Delphi i found that adding a line to shut up the code analyzer is not necessarily a bad idea. It expresses explicitly what would have happened anyway, so it improves the readability, not worsens it. Especially it fits well with assertions being a language feature in D. E.g. if one knows that a conditional on which execution path depends is necessarily zero, then one could either eliminate checks, or add an assert(var==0), which should be treated by a code analyzer as if variable was just assigned zero! -eye
Nov 29 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Ilya Minkov" <minkov cs.tum.edu> wrote in message
news:bqac81$2rl3$1 digitaldaemon.com...
 E.g. if one knows that a conditional on which execution path depends is
 necessarily zero, then one could either eliminate checks, or add an
 assert(var==0), which should be treated by a code analyzer as if
 variable was just assigned zero!

You're right that one advantage to having assertions as part of the syntax is that the optimizer can potentially make use of the additional information.
Nov 29 2003
parent reply Ilya Minkov <minkov cs.tum.edu> writes:
Walter wrote:
 You're right that one advantage to having assertions as part of the 
 syntax is that the optimizer can potentially make use of the 
 additional information.

I definately didn't mention the optimizer. What i meant is that assertion is a semantic guarantee, which may be very important to the code flow analysers, such as the one which makes sure that a function always retuns. So that if an analyser says that there is some execution path which doesn't return - it needn't place an assertion there, instead the programmer may place an assertion which makes sure that this code path never enters, which should be enough to keep the complaining analyser quite, and would make program flow more explicit. Let me also quote Roberto Mariottini, since his idea is of a similar nature:
 BTW, what about user defined enum? Can the compiler know whether all 
 enum constants are used inside a switch, and signal to the programmer
 when some are missing and there is not a default?

Even enum being stored within an int, this information translates into an implicit assertion (i==enumval1)||(i==enumval2)||(... which need not be generated in target code, but such information can be worked in by analysers as such. I'm probably bad at expressing my thoughts today, so i'll try to formulate it more thoroughly and post up some other time. This branch also goes somewhat off the thread topic, which was about switch. My opinion is that i completely agree with status quo which guards against unintended misuse very well, but i also find it an interesting option for a switch to requiere a default case, unless the code analyser could prove that such a case cannot happen. I would also vote for a requierement of explicit continue statement, which would give some symmetry with loops, but it may not be an option to you... -eye
Nov 30 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Ilya Minkov" <minkov cs.tum.edu> wrote in message
news:bqdqmg$1hen$1 digitaldaemon.com...
 Walter wrote:
 You're right that one advantage to having assertions as part of the
 syntax is that the optimizer can potentially make use of the
 additional information.

assertion is a semantic guarantee, which may be very important to the code flow analysers,

Well, that's just what an optimizer is: a code flow analyzer. <g>
 such as the one which makes sure that a function
 always retuns. So that if an analyser says that there is some execution
 path which doesn't return - it needn't place an assertion there, instead
 the programmer may place an assertion which makes sure that this code
 path never enters, which should be enough to keep the complaining
 analyser quite, and would make program flow more explicit.

You've just described the general idea of how optimizers work.
 Let me also quote Roberto Mariottini, since his idea is of a similar

 BTW, what about user defined enum? Can the compiler know whether all
 enum constants are used inside a switch, and signal to the programmer
 when some are missing and there is not a default?

Even enum being stored within an int, this information translates into an implicit assertion (i==enumval1)||(i==enumval2)||(... which need not be generated in target code, but such information can be worked in by analysers as such.

Even if all the enum values are accounted for, it's certainly possible the user cast some other value into the enum type.
 This branch also goes somewhat off the thread topic, which was about
 switch. My opinion is that i completely agree with status quo which
 guards against unintended misuse very well, but i also find it an
 interesting option for a switch to requiere a default case, unless the
 code analyser could prove that such a case cannot happen. I would also
 vote for a requierement of explicit continue statement, which would give
 some symmetry with loops, but it may not be an option to you...

Part of my reluctance to do that is implict fallthrough just has never been an issue for me in 25 years of programming. But the implicit default:break; has bitten me many times!
Nov 30 2003
next sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
 Part of my reluctance to do that is implict fallthrough just has never

 an issue for me in 25 years of programming. But the implicit

 has bitten me many times!

I can honestly say that neither issue has hurt me once in, say, the last five years. But what've either of our cat's whiskers got to do with it. There aren't many people who spend as much time as we on the keyboard - lucky bleeders! - so my question is: do you want your language to be a desirable option only for those who've been bitten for hard enough and long enough in older curmudgeonlier languages. There aren't that many of us left; most have slothed off into middle management by now. Surely you want to appeal to younger programmers. Since universities are not teaching much worth absorbing these days, why not build things into your language that help people who are of intermediate level, rather than just doing the bits you deem are needed for you and those like you(/us)? 1. If every aspect of switch is explicit and compile-time, how many users of D will be hurt? 0 2. If things remain implicit and, worse, there is runtime handling of code-time errors, how many users of D will be hurt? Probably about 20-40% now. Probably about 90-95% of the eventual target user base. If I'm wrong about this, can someone please tell me. I don't want to hear about any individual wants personally, we've already gone through that already. (Personally, I'd rather have it precisely have the semantics of C's switch, but I'd rather have D robust and successful than have to type a few less keystrokes.) Just point out where I'm wrong with these projections. (Notwithstanding the inherent inaccuracies in the precise numbers). Matthew
Nov 30 2003
parent "Sean L. Palmer" <palmer.sean verizon.net> writes:
I think your numbers are spot on.

For me, personally, it's been reversed.  Missing default:assert(0); hasn't
bitten me much, because any switch on an enum that I expect can grow already
has a default:assert(0); written in explicitly.  But the implicit
fallthrough has bitten me so many times it makes me scream every time it
happens (and it happens fairly frequently).

I know that the C fallthru behavior ends up on many a FAQ, and if you took a
poll of C users, they would rank it high on the list of bug sources,
probably just a few rungs down from dangling pointers, memory leaks, and
off-by-one errors.

Sean

"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bqefkr$2dnm$1 digitaldaemon.com...
 Part of my reluctance to do that is implict fallthrough just has never

 an issue for me in 25 years of programming. But the implicit

 has bitten me many times!

I can honestly say that neither issue has hurt me once in, say, the last five years. But what've either of our cat's whiskers got to do with it. There aren't many people who spend as much time as we on the keyboard - lucky bleeders! - so my question is: do you want your language to be a desirable option only for those who've been bitten for hard enough and

 enough in older curmudgeonlier languages. There aren't that many of us

 most have slothed off into middle management by now.

 Surely you want to appeal to younger programmers. Since universities are

 teaching much worth absorbing these days, why not build things into your
 language that help people who are of intermediate level, rather than just
 doing the bits you deem are needed for you and those like you(/us)?

 1. If every aspect of switch is explicit and compile-time, how many users

 D will be hurt? 0
 2. If things remain implicit and, worse, there is runtime handling of
 code-time errors, how many users of D will be hurt? Probably about 20-40%
 now. Probably about 90-95% of the eventual target user base.

 If I'm wrong about this, can someone please tell me. I don't want to hear
 about any individual wants personally, we've already gone through that
 already. (Personally, I'd rather have it precisely have the semantics of

 switch, but I'd rather have D robust and successful than have to type a

 less keystrokes.)

 Just point out where I'm wrong with these projections. (Notwithstanding

 inherent inaccuracies in the precise numbers).

 Matthew

Dec 01 2003
prev sibling parent reply Ilya Minkov <Ilya_member pathlink.com> writes:
In article <bqe96t$24um$1 digitaldaemon.com>, Walter says...

Well, that's just what an optimizer is: a code flow analyzer. <g>

I was advocating something different: it makes sense to do this as part of semantic analysis process, and bug user with arbitrary conditions like "i'm not convinced this function returns" or "this switch may not cover a variable value range", as long as convincing the compiler usually takes only one line, which would also make code more explicit. I'm only speaking of my experiance that i found this practice quite convenient. (Delphi fans vote here!) You must also consider that D advocates a different development cycle than C++. Recompile often and early. In this context it makes sense that the compiler bugs you about something not so important and not the runtime, especally the compiler being so fast. Your disagreement has probably something to do with your oposition to warnings, since that's what compiler message class this kind of diagnostic could best refer to. You really have to put up a paper on that so that we understand you better.
Even if all the enum values are accounted for, it's certainly possible the
user cast some other value into the enum type.

Should this be legal at all? If yes, then someone should take responsibility. Eother the cast has to take care that enum only gets a legal value by throwing an exception otherwise, or the programmer...
Part of my reluctance to do that is implict fallthrough just has never been
an issue for me in 25 years of programming. But the implicit default:break;
has bitten me many times!

And in the 5 years before the 25 years? You must also consider that you are an exceptional talent and a full-time programmer. I don't value language-specific skills that much... -eye
Dec 02 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Ilya Minkov" <Ilya_member pathlink.com> wrote in message
news:bqhnh3$15iv$1 digitaldaemon.com...
 I was advocating something different: it makes sense to do this as part of
 semantic analysis process, and bug user with arbitrary conditions like

 convinced this function returns" or "this switch may not cover a variable

 range", as long as convincing the compiler usually takes only one line,

 would also make code more explicit. I'm only speaking of my experiance

 found this practice quite convenient. (Delphi fans vote here!)

The trouble with 'may' is you wind up inserting some piece of cruft to get the compiler to pass it. That cruft is usually dead code, and since it does not contribute to the algorithm it serves merely to confuse the maintenance programmer. Two common pieces of such cruft are an extra return statement that's never executed, and supplying an initializer to a variable that is always overwritten before ever being read. Here's the extra initializer thing: int x; <= compiler demands that this needs an initializer if (...) x = 3; <= actual algorithmic initialization ... if (same condition as previous if) y = x; <= x is always initialized here to 3, but well-meaning but nagging compiler squawks it is possibly uninitialized. Argh. Inserting the extra initializer at the top just is confusing, as the maintenance programmer thinks "where is this value ever used? Looks like some sort of bug."
 You must also consider that D advocates a different development cycle than

 Recompile often and early. In this context it makes sense that the

 you about something not so important and not the runtime, especally the

 being so fast.

I agree with pushing as much off into compile time as makes sense to. But if I go too far, then we've got Pascal, and I gave up on that language after discovering that 50% of my coding time was spent fighting the compiler's best efforts to stop me from writing the kind of code I wanted to write.
 Your disagreement has probably something to do with your oposition to

 since that's what compiler message class this kind of diagnostic could

 refer to. You really have to put up a paper on that so that we understand

 better.

Perhaps I should rename "warnings" to "nags" and my rationale would be clearer <g>.
Even if all the enum values are accounted for, it's certainly possible


user cast some other value into the enum type.


Yes. A cast exists as an escape from the language's typing rules. Pascal didn't have a cast.
 If yes, then someone should take responsibility. Eother the cast has to

 care that enum only gets a legal value by throwing an exception otherwise,

 the programmer...

I expect the programmer to account for it, and if he fails to, hopefully the runtime will catch it and throw an exception.
Part of my reluctance to do that is implict fallthrough just has never


an issue for me in 25 years of programming. But the implicit


has bitten me many times!

You must also consider that you are an exceptional talent and a full-time programmer. I don't value

 skills that much...

I make plenty of coding errors, just like all of us here.
Dec 05 2003
next sibling parent reply one_mad_alien hotmail.com writes:
int x;    <= compiler demands that this needs an initializer
if (...)
    x = 3;    <= actual algorithmic initialization
...
if (same condition as previous if)
    y = x;    <= x is always initialized here to 3, but well-meaning but
nagging compiler squawks it is possibly uninitialized.

int x; if ( cond ) { x=3; } ... if ( cond || other cond ) { y = x; } and without warning we have a bug that the compiler is not telling us about. one solution would be int x = int.init; to keep the compiler happy (just explicitly doing what should be done anyway) a better would be int x = undef; in which case the debug build should change to; int x = int.init; int__x_set = false; if ( cond ) { x = 3; __x_set = true; } .... if ( cond || other ) { // or if (cond). if ( !__x_set ) { throw new Error( "x not initialised", __FILE__, __LINE__ ); } y = x; } // in a release build the 'int x = undef', is just 'int x = int.init' ;
Dec 06 2003
parent "Walter" <walter digitalmars.com> writes:
<one_mad_alien hotmail.com> wrote in message
news:bqsm17$287n$1 digitaldaemon.com...
 one solution would be
 int x = int.init;
 to keep the compiler happy (just explicitly doing what should be done

The compiler does this already implicitly if no initializer is given, then the optimizer usually optimizes it away as dead code.
 a better would be
 int x = undef;

For floating point types, the default initializer is NAN, and the result of all the computations on x will then be NAN. Unfortunately, there is no NAN value for integral types.
Dec 08 2003
prev sibling parent Georg Wrede <Georg_member pathlink.com> writes:
In article <bqs1ft$16i2$1 digitaldaemon.com>, Walter says...
"Ilya Minkov" <Ilya_member pathlink.com> wrote in message

(an excellent discussion deleted) If Walter succeeds in keeping the different phases of compiling separate, then that opens up (even commercial) possibilities for D. I could envision maybe getting an Open Source lexer, a commercial optimizer, and the code generator from a third place. Sort of like the OSI layer thing. This would of course only be possible if the layer interfaces were standardized. Then I could choose between a "nagging" or a "strict" front end, I could have an "errors only", "with warnings", "with remarks", or "with informative hints" front end, depending on whether the users are "absolute beginners" or "Ultra Pro Gurus". (There's more to this than just a verbosity level switch.) Heck, a university could even write front ends for different semester classes, and _especially_ one for non-CS-professors for their own work! For now, this implies that whatever error messages, or not, D this year generates is not all that important at the end of the day. Maybe we should concentrate more on actual language issues and other things with real long-term ramifications.
Dec 06 2003
prev sibling parent reply Georg Wrede <Georg_member pathlink.com> writes:
I've had a hard time reading this thread. Instead of a discussion,
this has more looked like Walter is getting an awful lot of flack
for not revamping case functionality.

I think that if a language implements exceptions, then with it goes
inseparably the feature that whatever the programmer has not
thought of (i.e. not caught), at least the runtime will offer a
graceful and informative exit.

In that same spirit, running off the end of functions, or not
having a default part in a case structure, are things the programmer
has not "thought of". So these should cause a runtime exception,
period. 

If that is intentional (as in "we _know_" this'll never happen),
then it is a small thing to inform the compiler that this is the
case. Also, this informs the next programmer about our intentions,
instead of having him think that we're just lazy SOBs.

Breaks in case statements exist because it is customary to have
both several different cases be handled by the same code, and also
because it is usual that one case only does a one-liner, the rest
of which is the same as some other case. Here the missing break
turns very useful.

Many things in D come from C(++), and many programmers will have
to work both in D and C(++), at least in the immediately foreseeable
future. Introducing gratuituos differences in the most basic things
would only make their life hard. Since the motivations presented
here (for making the changes in D) have not been able to show any
major advantages, I think we should let this issue rest for now.

(No flames for the following, please:) The fervor with which people
have argued for not having to write a couple extra words, has made
me think that maybe these guys do not do touch typing. I think 
such tings should not be permitted to influence the design of a
serious language for professional programming.

Even Bjarne Stroustrup has admitted that they made some changes
to the language so that it would be "easier to write, in this
(or that) particular context", and that some of these changes
became dragstones and were regretted later. One excellent example
would be declaring complicated pointer variables.
Nov 30 2003
parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
Sorry to be obtuse, but I don't glean what your position is? C/C++ status
quo, Walter's no-default exception, compiler-errors on any implicit parts?



"Georg Wrede" <Georg_member pathlink.com> wrote in message
news:bqd1l6$el6$1 digitaldaemon.com...
 I've had a hard time reading this thread. Instead of a discussion,
 this has more looked like Walter is getting an awful lot of flack
 for not revamping case functionality.

 I think that if a language implements exceptions, then with it goes
 inseparably the feature that whatever the programmer has not
 thought of (i.e. not caught), at least the runtime will offer a
 graceful and informative exit.

 In that same spirit, running off the end of functions, or not
 having a default part in a case structure, are things the programmer
 has not "thought of". So these should cause a runtime exception,
 period.

 If that is intentional (as in "we _know_" this'll never happen),
 then it is a small thing to inform the compiler that this is the
 case. Also, this informs the next programmer about our intentions,
 instead of having him think that we're just lazy SOBs.

 Breaks in case statements exist because it is customary to have
 both several different cases be handled by the same code, and also
 because it is usual that one case only does a one-liner, the rest
 of which is the same as some other case. Here the missing break
 turns very useful.

 Many things in D come from C(++), and many programmers will have
 to work both in D and C(++), at least in the immediately foreseeable
 future. Introducing gratuituos differences in the most basic things
 would only make their life hard. Since the motivations presented
 here (for making the changes in D) have not been able to show any
 major advantages, I think we should let this issue rest for now.

 (No flames for the following, please:) The fervor with which people
 have argued for not having to write a couple extra words, has made
 me think that maybe these guys do not do touch typing. I think
 such tings should not be permitted to influence the design of a
 serious language for professional programming.

 Even Bjarne Stroustrup has admitted that they made some changes
 to the language so that it would be "easier to write, in this
 (or that) particular context", and that some of these changes
 became dragstones and were regretted later. One excellent example
 would be declaring complicated pointer variables.

Nov 30 2003
parent reply Georg Wrede <Georg_member pathlink.com> writes:
In article <bqdjvu$1805$2 digitaldaemon.com>, Matthew Wilson says...
Sorry to be obtuse, but I don't glean what your position is? C/C++ status
quo, Walter's no-default exception, compiler-errors on any implicit parts?

(I was aspiring to transcend the current issues themselves, but what the heck.) Walter's no-default exception. I'm thinking of a couple of different situations here. First of all, if D is your first language and you're just learning to use the switch statement, then you normally write the cases you know can happen, but leave the default out (since you 'covered all cases'). If that is ok, then no compiler warning or error. Well, the day comes when you didn't cover all cases, and then there will be a runtime exception. Now, the same thing happens with uncaught exceptions. Most of the time your new programs compile and run fine, but then there's the (say file not found) exception, all of a sudden. So you learn to cover your bases by taking care of also other than the obvious situations. By this process, you learn the language (and programming, as such) in a nice and efficient, natural way. Second, if you already are a (good) programmer, then you just have all your cases covered. Or, your functions just don't run off the end when you don't intend it. In this case all is fine. If, OTOH you are a 'bad programmer', then that happens to you all the time. Then you learn by mistake (which is a way of life for such people anyhow), until you either get it, or become a marketing person instead. ;-) If it were obligatory to write the default statement, then these latter folks would just learn that that is something that has to be there, and obviously the line would be 'default: break;'. When their programs don't work the way they expect, then they have a very hard time finding out why. And since the compiler couldn't warn or error about this, the only way is to figure out why it acts other than we wished. Making the default case obligatory therefore achieves nothing. But making it 'entirely non-obligatory' doesn't either. (I.e., not causing a runtime exception.) Of course it is embarrassing if a professionally developed program suddenly quits on a no-default exception. But worse is just running off the end with no warning. It's like sweeping your crap under the rug. The rant about touch typing was (among other things) for the guys who wanted an implicit break in every case. Sure, it's nice to save ink, but the pros of it don't outweigh the cons.
Dec 01 2003
next sibling parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
The default, implicit, behavior should be the most common behavior needed by
the programmer.  For this reason, "in" parameters do not need explicitly
specified as "in".

break is much more common than fallthru, and would be even more so if case
supported ranges or multiple values.

Sean

"Georg Wrede" <Georg_member pathlink.com> wrote in message
news:bqf21n$81m$1 digitaldaemon.com...
 The rant about touch typing was (among other things) for the
 guys who wanted an implicit break in every case. Sure, it's nice
 to save ink, but the pros of it don't outweigh the cons.

Dec 01 2003
parent Felix <Felix_member pathlink.com> writes:
Yeah, I agree. I'd like to use smthng like:

switch a
case 1,3..8,15,99:
//etc....
default: 
//etc....
end

It is easier than the fallthrough approach....


In article <bqg1ki$1n9k$1 digitaldaemon.com>, Sean L. Palmer says...
The default, implicit, behavior should be the most common behavior needed by
the programmer.  For this reason, "in" parameters do not need explicitly
specified as "in".

break is much more common than fallthru, and would be even more so if case
supported ranges or multiple values.

Sean

"Georg Wrede" <Georg_member pathlink.com> wrote in message
news:bqf21n$81m$1 digitaldaemon.com...
 The rant about touch typing was (among other things) for the
 guys who wanted an implicit break in every case. Sure, it's nice
 to save ink, but the pros of it don't outweigh the cons.


Dec 01 2003
prev sibling parent reply Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bqf21n$81m$1 digitaldaemon.com>, Georg Wrede wrote:
 If it were obligatory to write the default statement, then these
 latter folks would just learn that that is something that has to be
 there, and obviously the line would be 'default: break;'. When their
 programs don't work the way they expect, then they have a very hard
 time finding out why. And since the compiler couldn't warn or error
 about this, the only way is to figure out why it acts other than we 
 wished. 

This reminds of the classic way Java novices learn to use exception specifications instead of the infamous "try ... catch { }" idiom. Step 1: "My program doesn't compile." "Wrap your functions in try statements and accompany them with empty catch statements." Step 2: "Now it compiles!" "Great! Did you try to run it?" Step 3: "Now it crashes..." "All right. Now let me tell you about exception specifications..." (It's funny 'cos it's true!) In an ideal language, the programmer should not be forced to write something "just because". Every line of code should mean something. "default: break;" does not say much, so maybe it would be best to just leave it unsaid? A single "return 0"; in the end of the function is not very informative either. C++ is overly verbose and tedious because it forces you to say "public" and "virtual" and "= 0" and "const" everywhere and demands that you maintain two copies of function and member function declarations (at worst three if you want insulation). And it forces you to say "break" all the time.
 The rant about touch typing was (among other things) for the
 guys who wanted an implicit break in every case. Sure, it's nice
 to save ink, but the pros of it don't outweigh the cons. 

Perhaps I'm reaching for the Dark Side by saying that the most common operation should be the default -- so that the programmer wouldn't have to type and read as much. In other word, I'm suggesting that the programmers should follow their natural tendency to be lazy. But on the other hand, shouldn't this principle be most satisfying to also the maintenance programmer, who would not be forced to search for the missing "break" in the switch statement (breaking is the most common operation, so why state the obvious, and the rare "fallthrough" or "goto case" would catch the reader's attention properly), and to spend time wondering if the return statement at the end of function was missing by purpose or by mistake? In short, requiring a "break" in a switch statement feels similar to requiring an empty "return" in the end of a function that does not in fact return anything. Of course, there are differing opinions as to what the most common operation in fact is. Some might even carry the opinion that in the switch statement, it's falling through -- obviously. Why else would C's switch statement fall through by default? Another feeling that comes with this is that in general, certain things should not happen silently. Such as throwing compiler-generated exceptions. If you want to make a function which throws an exception if the end of the function is reached, it would be in my opinion fair to force the programmer to write "assert(0)" or "throw ShouldNotBeHereException" or something similar before the end of the function. And if the programmer just happened to forget the return statement (or the exception), the compiler would immediately complain about it. -Antti
Dec 01 2003
parent "Sean L. Palmer" <palmer.sean verizon.net> writes:
Terse code is much easier to scan visually (think scanning a 1000-line
program written by a novice vs. an equivalent 100-line program written by a
guru)

In wordy code, there are more places for bugs to hide.

It is easy to maintain code that isn't there.

It is hard to remove code once it has been added.

IMHO, the shorter a piece of code is, the better, up to near the information
limit.  Simplify, simplify, simplify, until there is no more redundancy,
fluff, or contorted logic remaining.

Sean

"Antti Sykäri" <jsykari gamma.hut.fi> wrote in message
news:slrnbsn7ls.17n.jsykari pulu.hut.fi...
 In an ideal language, the programmer should not be forced to write
 something "just because". Every line of code should mean something.
 "default: break;" does not say much, so maybe it would be best to just
 leave it unsaid? A single "return 0"; in the end of the function is not
 very informative either. C++ is overly verbose and tedious because it
 forces you to say "public" and "virtual" and "= 0" and "const"
 everywhere and demands that you maintain two copies of function and
 member function declarations (at worst three if you want insulation).
 And it forces you to say "break" all the time.

 Perhaps I'm reaching for the Dark Side by saying that the most common
 operation should be the default -- so that the programmer wouldn't have
 to type and read as much. In other word, I'm suggesting that the
 programmers should follow their natural tendency to be lazy.  But on the
 other hand, shouldn't this principle be most satisfying to also the
 maintenance programmer, who would not be forced to search for the
 missing "break" in the switch statement (breaking is the most common
 operation, so why state the obvious, and the rare "fallthrough" or "goto
 case" would catch the reader's attention properly), and to spend time
 wondering if the return statement at the end of function was missing by
 purpose or by mistake?

 In short, requiring a "break" in a switch statement feels similar to
 requiring an empty "return" in the end of a function that does not in
 fact return anything.

 Of course, there are differing opinions as to what the most common
 operation in fact is. Some might even carry the opinion that in the
 switch statement, it's falling through -- obviously. Why else would C's
 switch statement fall through by default?

 Another feeling that comes with this is that in general, certain things
 should not happen silently.  Such as throwing compiler-generated
 exceptions.

 If you want to make a function which throws an exception if the end of
 the function is reached, it would be in my opinion fair to force the
 programmer to write "assert(0)" or "throw ShouldNotBeHereException" or
 something similar before the end of the function.

 And if the programmer just happened to forget the return statement (or
 the exception), the compiler would immediately complain about it.

 -Antti

Dec 01 2003
prev sibling parent reply Berin Loritsch <bloritsch d-haven.org> writes:
Walter wrote:

 Several C++ compilers do emit warnings for such. I find those warnings to be
 irritating rather than useful. For example, they will warn about:
 
     int foo()
     {
         while (1)
         {
                 ....
                     return ...;
         }
     }
 
 where it is *not possible* for the code to fall off the end, but the warning
 happens, and to get rid of it I have to put in a return statement that will
 never execute. I find being forced to add code that will never execute to be
 inelegant and potentially confusing. Hence my motivation for having D ensure
 that it never executes by raising an exception if it does, but that doesn't
 muck up the source code.

There are a couple things I really don't like about that code anyway. For instance, it is usually bad practice to return from inside a loop--your language has to be very good at cleaning up its stack in these cases. Second, if you want to loop forever until something important happens, use while(true) instead of a number. Explicit booleans can make conditionals absolutely clear. Lastly, if you want to break a loop early, that is what the break keyward is for. I have seen several programs where there was a similar "loop" in the code, but instead of while(1) it was while(0). In essense the code would never run, but because it is not commented out, you might waste time looking in there. Anyhoo, I really don't like that snippet above for my own sense of code style purposes. It really isn't that clear. I have read in many places that it is much better to have the principle of one place of exit. Instead of having something like this: if (foo < bar) return -1; if (foo > bar) return 1; return 0; It would be better overall to have something like this: int ordinal = 0; if (foo < bar) ordinal--; if (foo > bar) ordinal++; return ordinal; There are a couple of reasons, first of which is that there is a clear exit point that will always be taken. Second of which we are being a little more semantically clear in that we are comparing the relative order of two objects, and returning a value where the sign is more important than the value. 0 means the objects are equal, negative means the second object should come before the first, and positive means the second object should come after the second. (This is adapted from how Comparators work in Java). While the first example is technically legal, the maintainer who comes along afterwords has to figure out what is going on. In this example it is relatively clear. However, I have had to maintain some methods where the return was buried within several statements inside the conditional, and not really clear where it was. By enforcing the principle of one place of exit, the snippet you provided would be better written as: int foo() { while(true) { ..... break; } return ...; } Since that is the true intention of the code.
Dec 01 2003
parent "Walter" <walter digitalmars.com> writes:
"Berin Loritsch" <bloritsch d-haven.org> wrote in message
news:bqfiuc$vsf$1 digitaldaemon.com...
 Walter wrote:
 Several C++ compilers do emit warnings for such. I find those warnings


 irritating rather than useful. For example, they will warn about:

     int foo()
     {
         while (1)
         {
                 ....
                     return ...;
         }
     }

 where it is *not possible* for the code to fall off the end, but the


 happens, and to get rid of it I have to put in a return statement that


 never execute. I find being forced to add code that will never execute


 inelegant and potentially confusing. Hence my motivation for having D


 that it never executes by raising an exception if it does, but that


 muck up the source code.

instance, it is usually bad practice to return from inside a loop--

It must be supported even if it's bad practice.
 your language
 has to be very good at cleaning up its stack in these cases.

It isn't any extra work for the code generator <g>. The ugly problems are having returns inside try, finally or catch blocks.
 Second, if you
 want to loop forever until something important happens, use while(true)

 of a number.  Explicit booleans can make conditionals absolutely clear.

Ironically, some other compilers I use complain about any use of while with a constant, whereas the blessed form of an infinite loop is for(;;). Every team seems to have a different view of the right way to do an infinite loop; I prefer while(1) because there's no way that some idjit #define'd true to be 0. (And yes, in the old C days, you would find such definitions of true.) And yes, in D true is a keyword and can't be mucked up that way, but old habits die hard <g>.
 Lastly, if you want to break a loop early, that is what the break keyward

 for. Anyhoo, I really don't like that snippet above for my own sense of

 purposes.  It really isn't that clear.

Perhaps not, but these snippets I post are the essence of some considerably longer piece of code. I remove all the irrelevant cruft to get a minimalist example. This sometimes results in a snippet few people would actually write, but what I'm doing is trying to illustrate something that should be handled correctly by the compiler, not advocating it as good style.
 I have read in many places that it is much better to have the principle of

 place of exit.

You're right that there are many people who carefully follow the one exit rule. It is a perfectly reasonable style. But D isn't trying to enforce a particular style, it needs to be an easy migration path from C and C++ code, and such code uses returns in all kinds of weird places. If D forced a one exit rule on them, the potential users for D would shrink significantly.
Dec 09 2003
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter, I just wanted to weigh in here.  Your current design (implicit 
default throws and exception) is, IMHO, a Good Thing.

Having the programmer add
	default: assert(0);
is even better.

Thus, I would argue (I know, you hate warnings, but hear me out) that a 
warning that explicitly states you are adding the 
implicit-throw-on-default is a good idea.  It makes the C-porting task 
easier, and leads people toward better programming practice.
Nov 30 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:bqd01e$cc0$1 digitaldaemon.com...
 Walter, I just wanted to weigh in here.  Your current design (implicit
 default throws and exception) is, IMHO, a Good Thing.

At least somebody likes it <g>.
 Having the programmer add
 default: assert(0);
 is even better.

That's my normal practice in C/C++. Having that in there has saved me from countless bugs.
Dec 06 2003
parent reply Alix Pexton <Alix thedjournal.com> writes:
Walter wrote:

"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:bqd01e$cc0$1 digitaldaemon.com...
  

Walter, I just wanted to weigh in here.  Your current design (implicit
default throws and exception) is, IMHO, a Good Thing.
    

At least somebody likes it <g>.
Having the programmer add
default: assert(0);
is even better.
    

That's my normal practice in C/C++. Having that in there has saved me from countless bugs.

be controversial... Walter is absolutely RIGHT... If you don't your switch recieves value that is nonsensical, you should have an error thrown (one that told you that is what happened would be nicer than assert(0) ). If you add a value to a enum and miss one of the switches out in maintainance, then it will be exposed when you run your test suite, to which you added a test for complience with the revised enum (you do do write tests for your new code don't you <g>). I cannot see a problem with Walter's choice of implementation that is not due to bad programming practices (even good programmers can have bad practices). I myself am a bad programmer with good practices, aided by D's clever features... Alix... -- Alix Pexton Webmaster - http://www.theDjournal.com Alix theDjournal.com
Dec 07 2003
parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
I thinkWalter's way is better than not checking at all, but a few people on
this NG (including me) think it would be even better if the compiler just
complained at you that not all the cases were handled.  If the compiler can
tell that there are possible input values that aren't handled, it should
require a default clause.  I do not believe that enums that were typecasted
from ints should be considered.  If there is an error converting int to enum
it should be exposed at the point of the cast, not worried about during a
later switch.

switch (value)
{
    case 1:
        blah();
        break;
    case 2:
        foo();
        break;
    case 3:
        bar();
        break;
}

Now what the programmer *probably* meant was that if you get 1,2, or 3, do
those things, otherwise do nothing.  But it's not explicit in the code.  We
would want it to mandatorily be explicit.

Then if you want an assert, you put an assert, and if you do not, expecting
C's rules to hold, you don't get burned by asserts thrown once your
application ships, especially if the assert is thrown in a situation that
otherwise would be perfectly gracefully handled (because you really *did*
mean that if the case isn't matched, it should do nothing.)

Anytime there is a runtime check, there is a bug waiting to happen.  Make it
so you don't need the check, and there is no possibility of a bug at all.
That's better.

Sean

"Alix Pexton" <Alix thedjournal.com> wrote in message
news:bqvchc$l7l$1 digitaldaemon.com...
 I've been pondering this for a while, and I know that my view is going o
 be controversial...

 Walter is absolutely RIGHT...

 If you don't your switch recieves value that is nonsensical, you should
 have an error thrown (one that told you that is what happened would be
 nicer than assert(0) ).

 If you add a value to a enum and miss one of the switches out in
 maintainance, then it will be exposed when you run your test suite, to
 which you added a test for complience with the revised enum (you do do
 write tests for your new code don't you <g>).

 I cannot see a problem with Walter's choice of implementation that is
 not due to bad programming practices (even good programmers can have bad
 practices).

 I myself am a bad programmer with good practices, aided by D's clever
 features...

 Alix...

Dec 07 2003
next sibling parent "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
That's the most cogent opinion yet expressed. I heartily "hear, hear" it.

Anything else is insanity.

Thanks Sean

"Sean L. Palmer" <palmer.sean verizon.net> wrote in message
news:br040i$1mqc$1 digitaldaemon.com...
 I thinkWalter's way is better than not checking at all, but a few people

 this NG (including me) think it would be even better if the compiler just
 complained at you that not all the cases were handled.  If the compiler

 tell that there are possible input values that aren't handled, it should
 require a default clause.  I do not believe that enums that were

 from ints should be considered.  If there is an error converting int to

 it should be exposed at the point of the cast, not worried about during a
 later switch.

 switch (value)
 {
     case 1:
         blah();
         break;
     case 2:
         foo();
         break;
     case 3:
         bar();
         break;
 }

 Now what the programmer *probably* meant was that if you get 1,2, or 3, do
 those things, otherwise do nothing.  But it's not explicit in the code.

 would want it to mandatorily be explicit.

 Then if you want an assert, you put an assert, and if you do not,

 C's rules to hold, you don't get burned by asserts thrown once your
 application ships, especially if the assert is thrown in a situation that
 otherwise would be perfectly gracefully handled (because you really *did*
 mean that if the case isn't matched, it should do nothing.)

 Anytime there is a runtime check, there is a bug waiting to happen.  Make

 so you don't need the check, and there is no possibility of a bug at all.
 That's better.

 Sean

 "Alix Pexton" <Alix thedjournal.com> wrote in message
 news:bqvchc$l7l$1 digitaldaemon.com...
 I've been pondering this for a while, and I know that my view is going o
 be controversial...

 Walter is absolutely RIGHT...

 If you don't your switch recieves value that is nonsensical, you should
 have an error thrown (one that told you that is what happened would be
 nicer than assert(0) ).

 If you add a value to a enum and miss one of the switches out in
 maintainance, then it will be exposed when you run your test suite, to
 which you added a test for complience with the revised enum (you do do
 write tests for your new code don't you <g>).

 I cannot see a problem with Walter's choice of implementation that is
 not due to bad programming practices (even good programmers can have bad
 practices).

 I myself am a bad programmer with good practices, aided by D's clever
 features...

 Alix...


Dec 07 2003
prev sibling next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Sean L. Palmer wrote:
 Now what the programmer *probably* meant was that if you get 1,2, or 3, do
 those things, otherwise do nothing.  But it's not explicit in the code.  We
 would want it to mandatorily be explicit.
 
 Then if you want an assert, you put an assert, and if you do not, expecting
 C's rules to hold, you don't get burned by asserts thrown once your
 application ships, especially if the assert is thrown in a situation that
 otherwise would be perfectly gracefully handled (because you really *did*
 mean that if the case isn't matched, it should do nothing.)
 
 Anytime there is a runtime check, there is a bug waiting to happen.  Make it
 so you don't need the check, and there is no possibility of a bug at all.
 That's better.

I agree. Also, I would like to add one additonal point: What is the difference between a "switch" without a "default" and an "if" without an "else"? "If" and "switch" can both be used for the same thing, so why throw an exception when there is no "default", but not if the is not "else"? I believe the answer is personal coding style. It seems that Walter and a few others mostly use switch for things where they want to catch every possible condition. But it doesn't HAVE to be that way. Other programmers may want switch to behave simply like an "if-else if" chain without a final else. A good exmaple for this is a Windows WNDPROC procedure. In this you want to handle SOME of the messages in a special way, and pass others on to DefWindowProc. But you also quite often want to pass the handled messages to DefWindowProc as well. So such a procedure usually looks like this: LRESULT myWindowProc(....) { switch(message) { case WM_LBUTTONDOWN: doThis(); break; case WM....: doThat(); break; case WM...: doAnotherThing(); return 0; //don't call DefWindowProc break; } return CallWindowProc(DefWindowProc,...); } In my opinion. that's a perfectly valid way to use a switch. Throwing an exception by default would cause quite some bugs, I think. IMHO the best thing is to require the default clause, as was suggested multiple times. That way no one can be surprised by unexpected behaviour of the compiler. Hauke
 
 Sean
 
 "Alix Pexton" <Alix thedjournal.com> wrote in message
 news:bqvchc$l7l$1 digitaldaemon.com...
 
I've been pondering this for a while, and I know that my view is going o
be controversial...

Walter is absolutely RIGHT...

If you don't your switch recieves value that is nonsensical, you should
have an error thrown (one that told you that is what happened would be
nicer than assert(0) ).

If you add a value to a enum and miss one of the switches out in
maintainance, then it will be exposed when you run your test suite, to
which you added a test for complience with the revised enum (you do do
write tests for your new code don't you <g>).

I cannot see a problem with Walter's choice of implementation that is
not due to bad programming practices (even good programmers can have bad
practices).

I myself am a bad programmer with good practices, aided by D's clever
features...

Alix...


Dec 08 2003
next sibling parent "Lars Ivar Igesund" <larsivar igesund.net> writes:
Good point. Especially since it is possible to switch on strings, but as
long
as default is required, that is no real problem (as you also point out). In
my
ddepcheck program, I use a switch to check the command line switches. A
few of them aren't as easy as doing a case on them, so I first switch on the
easy ones (having 'default: break;' at the end), then I continue with some
ifs
and stuff.

I expect dmd 0.77 to flag a compile error when encountering switches
without default. :)

Lars Ivar Igesund

"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
news:br1di4$m1o$1 digitaldaemon.com...
 Sean L. Palmer wrote:
 Now what the programmer *probably* meant was that if you get 1,2, or 3,


 those things, otherwise do nothing.  But it's not explicit in the code.


 would want it to mandatorily be explicit.

 Then if you want an assert, you put an assert, and if you do not,


 C's rules to hold, you don't get burned by asserts thrown once your
 application ships, especially if the assert is thrown in a situation


 otherwise would be perfectly gracefully handled (because you really


 mean that if the case isn't matched, it should do nothing.)

 Anytime there is a runtime check, there is a bug waiting to happen.


 so you don't need the check, and there is no possibility of a bug at


 That's better.

I agree. Also, I would like to add one additonal point: What is the difference between a "switch" without a "default" and an "if" without an "else"? "If" and "switch" can both be used for the same thing, so why throw an exception when there is no "default", but not if the is not "else"? I believe the answer is personal coding style. It seems that Walter and a few others mostly use switch for things where they want to catch every possible condition. But it doesn't HAVE to be that way. Other programmers may want switch to behave simply like an "if-else if" chain without a final else. A good exmaple for this is a Windows WNDPROC procedure. In this you want to handle SOME of the messages in a special way, and pass others on to DefWindowProc. But you also quite often want to pass the handled messages to DefWindowProc as well. So such a procedure usually looks like this: LRESULT myWindowProc(....) { switch(message) { case WM_LBUTTONDOWN: doThis(); break; case WM....: doThat(); break; case WM...: doAnotherThing(); return 0; //don't call DefWindowProc break; } return CallWindowProc(DefWindowProc,...); } In my opinion. that's a perfectly valid way to use a switch. Throwing an exception by default would cause quite some bugs, I think. IMHO the best thing is to require the default clause, as was suggested multiple times. That way no one can be surprised by unexpected behaviour of the compiler. Hauke
 Sean

 "Alix Pexton" <Alix thedjournal.com> wrote in message
 news:bqvchc$l7l$1 digitaldaemon.com...

I've been pondering this for a while, and I know that my view is going o
be controversial...

Walter is absolutely RIGHT...

If you don't your switch recieves value that is nonsensical, you should
have an error thrown (one that told you that is what happened would be
nicer than assert(0) ).

If you add a value to a enum and miss one of the switches out in
maintainance, then it will be exposed when you run your test suite, to
which you added a test for complience with the revised enum (you do do
write tests for your new code don't you <g>).

I cannot see a problem with Walter's choice of implementation that is
not due to bad programming practices (even good programmers can have bad
practices).

I myself am a bad programmer with good practices, aided by D's clever
features...

Alix...



Dec 08 2003
prev sibling parent reply Ant <Ant_member pathlink.com> writes:
In article <br1di4$m1o$1 digitaldaemon.com>, Hauke Duden says...
What is the difference between a "switch" without a "default" and an 
"if" without an "else"?

"If" and "switch" can both be used for the same thing, so why throw an 
exception when there is no "default", but not if the is not "else"?

ah! ah! let's see Walter dodging this one ;) Ant
Dec 08 2003
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Ant wrote:

In article <br1di4$m1o$1 digitaldaemon.com>, Hauke Duden says...
  

What is the difference between a "switch" without a "default" and an 
"if" without an "else"?

"If" and "switch" can both be used for the same thing, so why throw an 
exception when there is no "default", but not if the is not "else"?

    

ah! ah! let's see Walter dodging this one ;) Ant

---------------- if (x == 2) ... else if (x == 5) ... //No else things run fine, therefore no assertion needed ---------------- switch (x) { case 2: ... //On no, forgot the break. But it's ok, because the compiler (might) tell me at runtime, when it falls through to default. case 5: .. //Forget the break here as well. -> if you do it once, you might do it twice. } I'd prefer compile time errors that enforce no-fall-though at compile-time (various structures have been offered) rather then -> maybe-crash-at-runtime when I least suspect it runtime-assertions.
Dec 08 2003
next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
J Anderson wrote:
 What is the difference between a "switch" without a "default" and an 
 "if" without an "else"?

 "If" and "switch" can both be used for the same thing, so why throw 
 an exception when there is no "default", but not if the is not "else"?

   

ah! ah! let's see Walter dodging this one ;) Ant

switch (x) { case 2: ... //On no, forgot the break. But it's ok, because the compiler (might) tell me at runtime, when it falls through to default. case 5: .. //Forget the break here as well. -> if you do it once, you might do it twice. }

You are trying to use one flaw to justify another! But since this "proves" that D should to require an explicit default AND should have an implicit break I'll let it pass ;). Hauke
Dec 08 2003
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Hauke Duden wrote:

 J Anderson wrote:

 What is the difference between a "switch" without a "default" and 
 an "if" without an "else"?

 "If" and "switch" can both be used for the same thing, so why throw 
 an exception when there is no "default", but not if the is not "else"?

   

ah! ah! let's see Walter dodging this one ;) Ant

switch (x) { case 2: ... //On no, forgot the break. But it's ok, because the compiler (might) tell me at runtime, when it falls through to default. case 5: .. //Forget the break here as well. -> if you do it once, you might do it twice. }

You are trying to use one flaw to justify another! But since this "proves" that D should to require an explicit default AND should have an implicit break I'll let it pass ;). Hauke

For: switch (x) { case 2: ... //On no, forgot the break. But it's ok, because the compiler (might) tell me at runtime, when it falls through to default. case 5: .. //Forget the break here as well. -> if you do it once, you might do it twice. } What the coder meant to write was: switch (x) { case 2: ... //On no, forgot the break. But it's ok, because the compiler (might) tell me at runtime, when it falls through to default. break; case 5: .. //Forget the break here as well. -> if you do it once, you might do it twice. break; } If you had an explicit break coders may tend to write something like: switch (x) { case 2: ... //On no, forgot the break. But it's ok, because the compiler (might) tell me at runtime, when it falls through to default. case 5: .. //Forget the break here as well. -> if you do it once, you might do it twice. default: //On no, there's not even a runtime warning. } Which is why Walter (in C) puts in the assert, for the default. D does this automaticly (why write what the compiler already does?) At least you have a run-time warning if you forget to put in the breaks. So, it's not an argument for explicit default.
Dec 08 2003
parent Hauke Duden <H.NS.Duden gmx.net> writes:
J Anderson wrote:
 For:
 <switch without breaks> 
 
 What the coder meant to write was:
 <switch with breaks>
 
 If you had an explicit break coders may tend to write something like:
 
 switch (x)
 {
  case 2:
  ... //On no, forgot the break. But it's ok, because the compiler 
 (might) tell me at runtime, when it falls through to default.
  case 5:
  .. //Forget the break here as well. -> if you do it once, you might do 
 it twice.
    default:
    //On no, there's not even a runtime warning.
 }
 
 Which is why Walter (in C) puts in the assert, for the default.  D does 
 this automaticly (why write what the compiler already does?) At least 
 you have a run-time warning if you forget to put in the breaks.
 
 So, it's not an argument for explicit default.
 

What I said was that D should have both a IMplicit break and an EXplicit default (actually, I'd prefer if a missing default had no effect at all - no exception and no error - but I'd settle for the explicit one. It's still much better than the runtime exception.). With implicit breaks your example could never happen, since the breaks would always be (implicitly) there. Apart from that, I think that arguing that the "default" exception can be used to catch missing breaks is a little too constructed. It'd only work if the missing break was the very last one. And even if it is you'd get a misleading error message. I.e. you'd think that you got an unexpected switch value, not that you got an expected one and your code was just missing a break. Hauke
Dec 08 2003
prev sibling parent Juan C. <Juan_member pathlink.com> writes:
Oh, well then you'll love this:

I recently came up with a different approach to if...

As you may recall, I always put braces on all my ifs, fors, whiles, etc. It
occurred to me that I could create a language (after all any old fool can do it
-- hee hee) that has an if like this --

if expr
{

// then-part

§   // in case that didn't go right, that's the "section" mark 

// else-part

}

All three symbols are required and therefor you can't have an if without an else
(although of course it may be empty).

In article <br29ht$20j9$1 digitaldaemon.com>, J Anderson says...
Ant wrote:

In article <br1di4$m1o$1 digitaldaemon.com>, Hauke Duden says...
  

What is the difference between a "switch" without a "default" and an 
"if" without an "else"?

"If" and "switch" can both be used for the same thing, so why throw an 
exception when there is no "default", but not if the is not "else"?

    

ah! ah! let's see Walter dodging this one ;) Ant

---------------- if (x == 2) ... else if (x == 5) ... //No else things run fine, therefore no assertion needed ---------------- switch (x) { case 2: ... //On no, forgot the break. But it's ok, because the compiler (might) tell me at runtime, when it falls through to default. case 5: .. //Forget the break here as well. -> if you do it once, you might do it twice. } I'd prefer compile time errors that enforce no-fall-though at compile-time (various structures have been offered) rather then -> maybe-crash-at-runtime when I least suspect it runtime-assertions.

Dec 08 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <palmer.sean verizon.net> wrote in message
news:br040i$1mqc$1 digitaldaemon.com...
 I thinkWalter's way is better than not checking at all, but a few people

 this NG (including me) think it would be even better if the compiler just
 complained at you that not all the cases were handled.

This is equivalent to requiring that an explicit default statement always be provided. There's no way the compiler can prove all cases were handled.
 If the compiler can
 tell that there are possible input values that aren't handled, it should
 require a default clause.

There's just no way for the compiler to do this.
 I do not believe that enums that were typecasted
 from ints should be considered.  If there is an error converting int to

 it should be exposed at the point of the cast, not worried about during a
 later switch.

Casting ints to enums should not be an error. Consider, for example, gathering input from a data file and converting it to an enum. If the cast is made illegal, then the programmer will just do it another way, such as type painting or using an enum. Other values can also creep in from coding bugs elsewhere, pointer bugs, etc.
 switch (value)
 {
     case 1:
         blah();
         break;
     case 2:
         foo();
         break;
     case 3:
         bar();
         break;
 }

 Now what the programmer *probably* meant was that if you get 1,2, or 3, do
 those things, otherwise do nothing.  But it's not explicit in the code.

 would want it to mandatorily be explicit.

I view the above switch statement as explicitly saying "the only possible values for value are 1, 2 or 3. No other values are possible. Therefore, it is a program bug if there are any other values." This also means the D optimizer is allowed to take advantage of the knowledge after the end of the switch that value can only have the values 1, 2, or 3. If an explicit default is given, that means that other values are possible.
 Then if you want an assert, you put an assert, and if you do not,

 C's rules to hold, you don't get burned by asserts thrown once your
 application ships, especially if the assert is thrown in a situation that
 otherwise would be perfectly gracefully handled (because you really *did*
 mean that if the case isn't matched, it should do nothing.)

The throwing of the switch default exception is not causing a bug in the program. It is detecting a bug in a manner that is more reasonable than the random, erratic behavior caused by unanticipated input. DM compilers are loaded up with such runtime checking; I'm sure you're all familiar with the messages it produces! I'm sure you've also noticed that the compiler rarely crashes as a result of a bug, it usually semi-gracefully quits with an internal error message. I much prefer the latter to the former.
 Anytime there is a runtime check, there is a bug waiting to happen.

I don't agree, since I think it's equivalent to saying that stripping all error checking code out of a program will improve its reliability. The whole idea of Design by Contract is to validate various assertions about the state of the program. Invalid states are supposed to cause the program to terminate gracefully. Unanticipated values in a switch statement certainly are an invalid state of the program, and checking for them is a (small) part of DbC.
 Make it
 so you don't need the check, and there is no possibility of a bug at all.
 That's better.

I certainly agree with that. But I think that is not our point of disagreement - our disagreement is whether the runtime check should be put in by the programmer or by the compiler. If you explicitly insert default:assert(0); there's still a runtime check! I'll reiterate that there's no way that the compiler can prove that all cases are covered without a default statement. You have an excellent point that the semantics differ from C, where the implicitly generated default statement is default:break; rather than default:assert(0);. Interestingly, from a code generation perspective, you can save a bit of space and time by having no default case at all. This is possible in D (it is not possible in C) when compiled with runtime checking turned off.
Dec 08 2003
parent reply Patrick Down <Patrick_member pathlink.com> writes:
In article <br3s0u$19rs$1 digitaldaemon.com>, Walter says...
 switch (value)
 {
     case 1:
         blah();
         break;
     case 2:
         foo();
         break;
     case 3:
         bar();
         break;
 }

 Now what the programmer *probably* meant was that if you get 1,2, or 3, do
 those things, otherwise do nothing.  But it's not explicit in the code.

 would want it to mandatorily be explicit.

I view the above switch statement as explicitly saying "the only possible values for value are 1, 2 or 3. No other values are possible. Therefore, it is a program bug if there are any other values." This also means the D optimizer is allowed to take advantage of the knowledge after the end of the switch that value can only have the values 1, 2, or 3. If an explicit default is given, that means that other values are possible.

Yes I would view the switch statement the same way. There are three ways of looking at this. 1) No 'default' means that all other values are an error. The programmer should explicitly state that other values are not an error condition by putting default: break; 2) No 'default' means that other values are acceptable. The programmer must explicitly state that other values are an error condition by putting default: assert(0); 3) 'No default' is a compiler error. The programmer must explicitly state how other values are handled. As far as explicitly stating the programmers intention all three work equally well when combined with firm documentation of the default behavior of the switch statement. All that option 3 really does is save the programmer from an error of omission. In other words the programmer really intended the opposite behavior from what the default is but forgot to include the 'default' statement. How is this any different that any other error of omission? Are you more likely to forget a 'default' in a 'switch' than a 'else' on a 'if'? Now as for choosing between options 1 and 2. It makes sense to me that a switch lists the values that are handled, not the values that are not handled. So if a value is not in the list it is an error. 'default' means I handle every other value not explicitly listed. As for throwing a runtime error for the implicit default case it is really very similar to the runtime error for an array index out of bounds. I don't have any problem with this behavior. So I'm voting for Walter's current implementation.
Dec 09 2003
parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Patrick Down wrote:
 There are three ways of looking at this.
 
 1) No 'default' means that all other values are an 
 error.  The programmer should explicitly state that 
 other values are not an error condition by putting 
 default: break;
 
 2) No 'default' means that other values are acceptable.
 The programmer must explicitly state that other values
 are an error condition by putting default: assert(0);
 
 3) 'No default' is a compiler error.  The programmer 
 must explicitly state how other values are handled.  
 
 As far as explicitly stating the programmers intention
 all three work equally well when combined with firm 
 documentation of the default behavior of the switch 
 statement.  All that option 3 really does is save the 
 programmer from an error of omission.  In other words 
 the programmer really intended the opposite behavior 
 from what the default is but forgot to include the 
 'default' statement.  How is this  any different that 
 any other error of omission?  Are you more likely to 
 forget a 'default' in a 'switch' than a 'else' on a 
 'if'?

There is one important difference: it works differently in all other major languages. I programmed in C, C++ and JAVA for many years and I fear that I WILL forget to put that default in there. D's syntax is so similar in many respects and the construct even has the same name! So this is an accident waiting to happen. But the worst thing about this is that when that accident happens I might not know it until it is too late (i.e. when the application is shipped and some user has done something that causes the default case to trigger). That's why I prefer the explicit default. No matter what you think is intuitive (and we have seen that this IS different from person to person), the compiler will warn you right when you made your mistake. Then you just type an additional line and there's no harm done. Compare that to the case where I have to find the bug after a user has told me "Your software thingy crashed! I didn't do anything!". Hauke
Dec 09 2003
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Hauke Duden wrote:

 Patrick Down wrote:

 There are three ways of looking at this.

 1) No 'default' means that all other values are an error.  The 
 programmer should explicitly state that other values are not an error 
 condition by putting default: break;

 2) No 'default' means that other values are acceptable.
 The programmer must explicitly state that other values
 are an error condition by putting default: assert(0);

 3) 'No default' is a compiler error.  The programmer must explicitly 
 state how other values are handled. 
 As far as explicitly stating the programmers intention
 all three work equally well when combined with firm documentation of 
 the default behavior of the switch statement.  All that option 3 
 really does is save the programmer from an error of omission.  In 
 other words the programmer really intended the opposite behavior from 
 what the default is but forgot to include the 'default' statement.  
 How is this  any different that any other error of omission?  Are you 
 more likely to forget a 'default' in a 'switch' than a 'else' on a 'if'?

There is one important difference: it works differently in all other major languages. I programmed in C, C++ and JAVA for many years and I fear that I WILL forget to put that default in there. D's syntax is so similar in many respects and the construct even has the same name! So this is an accident waiting to happen. But the worst thing about this is that when that accident happens I might not know it until it is too late (i.e. when the application is shipped and some user has done something that causes the default case to trigger). That's why I prefer the explicit default. No matter what you think is intuitive (and we have seen that this IS different from person to person), the compiler will warn you right when you made your mistake. Then you just type an additional line and there's no harm done. Compare that to the case where I have to find the bug after a user has told me "Your software thingy crashed! I didn't do anything!". Hauke

Do you send out distributions that are compiled in debug mode? In release mode an assert won't occur. If you have an explicit default, you still don't know if you've missed a case value. All you know is that you've handled all cases, which is a different thing.
Dec 09 2003
parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
J Anderson wrote:
 Do you send out distributions that are compiled in debug mode? In 
 release mode an assert won't occur.

Problem is, D doesn't do an assert, it throws an exception (SwitchException, if I recall it correctly). Walter just wrote default;assert(0); "metaphorically" in this discussion (he said so somewhere himself). Hauke
Dec 10 2003
parent reply Patrick Down <pat codemoon.com> writes:
Hauke Duden <H.NS.Duden gmx.net> wrote in news:br6oio$2pf2$1
 digitaldaemon.com:

 J Anderson wrote:
 Do you send out distributions that are compiled in debug mode? In 
 release mode an assert won't occur.

Problem is, D doesn't do an assert, it throws an exception (SwitchException, if I recall it correctly). Walter just wrote default;assert(0); "metaphorically" in this discussion (he said so somewhere himself).

Actaully these execptions are compliled out in the release version, just like the array bounds checking. ( Walter correct me if I'm wrong. )
Dec 10 2003
next sibling parent reply Juan C. <Juan_member pathlink.com> writes:
I still don't think that the compiler should spend its time checking for poor or
perhaps-mistake-ridden code. That should be a job for some other utility. The
compiler should merely compile what its given, only confirming that the code is
legal. It seems hypocritical to me that you want to fill up the compiler with
all sorts of bloat and then complain that builds take too long.

<And, yes, I'm talking about your  if (x = y )  being illegal bit of nonsense.>

A file doesn't need to be checked for rookie mistakes every time it's compiled,
only when it's changed. If we write and check a file and then put it into a code
library, why check it again when it's pulled out for inclusion in a new project?

If D is to be for beginners, then OK, put training wheels on it. But if it's to
be for experienced power programmers who write expressively and perhaps
obfuscatedly (?), then trust us to know what we're doing!

Or maybe add an /expert or /fast switch to the command line, or have /release
not check such things?


In article <Xns944D595474DC9patcodemooncom 63.105.9.61>, Patrick Down says...
Hauke Duden <H.NS.Duden gmx.net> wrote in news:br6oio$2pf2$1
 digitaldaemon.com:

 J Anderson wrote:
 Do you send out distributions that are compiled in debug mode? In 
 release mode an assert won't occur.

Problem is, D doesn't do an assert, it throws an exception (SwitchException, if I recall it correctly). Walter just wrote default;assert(0); "metaphorically" in this discussion (he said so somewhere himself).

Actaully these execptions are compliled out in the release version, just like the array bounds checking. ( Walter correct me if I'm wrong. )

Dec 10 2003
parent Ilya Minkov <minkov cs.tum.edu> writes:
We're very far from experiencing any speed problems at compilation.

These checks simply don't need any significant time.

Juan C. wrote:
 A file doesn't need to be checked for rookie mistakes every time it's compiled,
 only when it's changed. If we write and check a file and then put it into a
code
 library, why check it again when it's pulled out for inclusion in a new
project?

When you compile the library, you need to place the source into src. And then incude the binary when linking. However, the source is generally unused when compiling other modules, only declarations are extracted, the code itself is added by the linker. For efficiency reasons, source code can be taken for inlining from src, but nontheless no warnings about the code should be output. BTW, you can compile a library from source, then install "headers" stripped of function bodies into src. This also works.
 If D is to be for beginners, then OK, put training wheels on it. But if it's to
 be for experienced power programmers who write expressively and perhaps
 obfuscatedly (?), then trust us to know what we're doing!

D should definately be good for beginners. Professionals have C++: a language which is somewhat complicated but fascinating and wonderful as you start to get it. But one which calls for trouble if you just begin to learn it. Obfuscated code is to be more or less forbidden in D. Look at Walter's code, it's very clean. It looks more or less like code a Delphi programmer would write. :)
 Or maybe add an /expert or /fast switch to the command line, or have /release
 not check such things?

You know that Walter is strongly against such things. Compiler switches should not change the sematics of the language, else we shall have portability problems among build environments. Anyway, who cares how long release compiles take? And by the way, the back-end takes itself quite some time when you want to compile with optimisations. -eye PS. Walter, wouldn't you call this if(x=y) check a "nag"? No, i'm not against it, but i was questioning consistency.
Dec 10 2003
prev sibling parent J Anderson <REMOVEanderson badmama.com.au> writes:
Patrick Down wrote:

Hauke Duden <H.NS.Duden gmx.net> wrote in news:br6oio$2pf2$1
 digitaldaemon.com:

  

J Anderson wrote:
    

Do you send out distributions that are compiled in debug mode? In 
release mode an assert won't occur.
      

(SwitchException, if I recall it correctly). Walter just wrote default;assert(0); "metaphorically" in this discussion (he said so somewhere himself).

Actaully these execptions are compliled out in the release version, just like the array bounds checking. ( Walter correct me if I'm wrong. )

Dec 10 2003
prev sibling next sibling parent "Lars Ivar Igesund" <larsivar igesund.net> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bq2vi2$tqd$1 digitaldaemon.com...
 We should have also an option to specify that each possible
 values must have a label for case when all cases must be
 handled. This would typically be usefull for enumerations
 where we might want to ensure that all values appears.

A good idea, but this is already addressed in D where the default case,


 not specified, is to throw an exception.

Wow! That's a *really*, *REALLY* bad idea.

I agree. Lars Ivar Igesund
Nov 26 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bq2vi2$tqd$1 digitaldaemon.com...
 A good idea, but this is already addressed in D where the default case,


 not specified, is to throw an exception.

If one is porting some code which very rarely uses the default case - it's largely irrelevant whether that's benign or a bug - it may easily escape

 test cases, and "sometime later", in production code, an exception will be
 thrown which has never been anticipated and therefore never been handled.

If it's never been anticipated and never been handled, then it's a program bug. What the exception does is give a controlled failure of the program, rather than an unpredictable erratic behavior.
 This is about the very best/worst example I could possibly think of in the
 debate between compile-time and runtime error-handling. Rather than

 me 30 seconds in development time, I am up for an unknown, and potentially
 huge, cost sometime after deployment. Is D really wanting to be a serious
 systems language?

A robust system programming language *should* give an exception on an unanticipated, unhandled situation.
Nov 27 2003
parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
 A good idea, but this is already addressed in D where the default



 if
 not specified, is to throw an exception.

If one is porting some code which very rarely uses the default case -


 largely irrelevant whether that's benign or a bug - it may easily escape

 test cases, and "sometime later", in production code, an exception will


 thrown which has never been anticipated and therefore never been


 If it's never been anticipated and never been handled, then it's a program
 bug. What the exception does is give a controlled failure of the program,
 rather than an unpredictable erratic behavior.

This is just plain wrong. What you're saying is that D must reflect the bugs inherent in code that is ported in from other languages, so kind of admitting defeat, or that D should facilitate, nay encourage, the shit practises of people who have abused weaknesses in other languages.
 This is about the very best/worst example I could possibly think of in


 debate between compile-time and runtime error-handling. Rather than

 me 30 seconds in development time, I am up for an unknown, and


 huge, cost sometime after deployment. Is D really wanting to be a


 systems language?

A robust system programming language *should* give an exception on an unanticipated, unhandled situation.

A robust system programming language should favour compile time error fixes than runtime ones. Since exceptions are a questionable error-handling mechanism at best (flame me now, you "modern" programmers!!!) you're locking people into having to care about arbitrary bugs, in arbitrary modules (that may be other people's libraries), when this could all be obviated by the compiler doing marginally better than those for languages D that purports to be an improvement over. Sorry, but, much as I love and respect the very air that you breath Walter ;), this is just utter folly. Bertie Betterment
Nov 27 2003
parent reply "Charles Sanders" <sanders-consulting comcast.net> writes:
Have to agree here, I rareley ( never ? ) use exceptions , and dont like
them being thrown without my knoweldge / consent.



C

"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bq5m7f$2023$1 digitaldaemon.com...
 A good idea, but this is already addressed in D where the default



 if
 not specified, is to throw an exception.

If one is porting some code which very rarely uses the default case -


 largely irrelevant whether that's benign or a bug - it may easily



 the
 test cases, and "sometime later", in production code, an exception



 be
 thrown which has never been anticipated and therefore never been


 If it's never been anticipated and never been handled, then it's a


 bug. What the exception does is give a controlled failure of the


 rather than an unpredictable erratic behavior.

This is just plain wrong. What you're saying is that D must reflect the

 inherent in code that is ported in from other languages, so kind of
 admitting defeat, or that D should facilitate, nay encourage, the shit
 practises of people who have abused weaknesses in other languages.

 This is about the very best/worst example I could possibly think of in


 debate between compile-time and runtime error-handling. Rather than

 me 30 seconds in development time, I am up for an unknown, and


 huge, cost sometime after deployment. Is D really wanting to be a


 systems language?

A robust system programming language *should* give an exception on an unanticipated, unhandled situation.

A robust system programming language should favour compile time error

 than runtime ones.

 Since exceptions are a questionable error-handling mechanism at best

 me now, you "modern" programmers!!!) you're locking people into having to
 care about arbitrary bugs, in arbitrary modules (that may be other

 libraries), when this could all be obviated by the compiler doing

 better than those for languages D that purports to be an improvement over.

 Sorry, but, much as I love and respect the very air that you breath Walter
 ;), this is just utter folly.

 Bertie Betterment

Nov 27 2003
parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
 Have to agree here, I rareley ( never ? ) use exceptions , and dont like
 them being thrown without my knoweldge / consent.

Exactamundo, my friend. Exceptions are great for somethings, including actual serious/fatal errors, or when doing stuff like deep-level parsing. Where they are absolutely not appropriate - and I can't believe this is even a discusson - is a runtime report of a code-time mistake!!
Nov 27 2003
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bq65gu$2n8o$1 digitaldaemon.com...
 Have to agree here, I rareley ( never ? ) use exceptions , and dont like
 them being thrown without my knoweldge / consent.

Exactamundo, my friend. Exceptions are great for somethings, including actual serious/fatal

 or when doing stuff like deep-level parsing.

 Where they are absolutely not appropriate - and I can't believe this is

 a discusson - is a runtime report of a code-time mistake!!

There's no way to detect the error at compile time. What it's for is when one has: #1 switch (x) { case 1: ... case 2: ... case 3: ... } and then one day x has the value 4. In C, there's an implicit default:break; inserted when no default is explicitly supplied. But in my experience coding, debugging my own mistakes, and fixing other peoples' code, the implicit default break is almost always the WRONG thing to happen, and then something unexpected happens as a result (i.e. crash, data corruption, etc.). When I see C code like that, and can ask the author, the intention is that x will always be 1, 2 or 3 and NEVER anything else. That's why he wrote the switch that way. That's ok, but then the maintenance programmer adds a feature where x is 4, updates all the switch statements accordingly, but inevitably misses one in the 100,000 line program he's revising. Even if the original coder INTENDED to make use of the implicit default:break;, it has that distinct odor of a bug, and so I flag it in code reviews. And in my experience, it was never intended. For years, I've advocated 'defensive programming' in C by making it explicit that the default case can never happen with: #2 switch (x) { case 1: ... case 2: ... case 3: ... default: assert(0); } This is the normal practice in my own code. There are 3 situations to deal with, the above one, and: #3 switch (x) { case 1: ... case 2: ... case 3: ... default: break; } and: #4 switch (x) { case 1: ... case 2: ... case 3: ... default: do something break; } What D does is make it easy to ensure that all the bases are covered by generating #2 if a default is not supplied. You're not going to get caught with a random crash from forgetting to deal with a case. The compiler cannot do this at compile time because it has no way of determining all possible values x can take. In that way, it's similar to run time array bounds checking. The only other way of doing this with a hope of robustness is to *require* an explicitly written default statement for every switch. This would certainly be a valid language strategy, but I don't really want to be nagged by the compiler to insert a default:assert(0); when I know darn well that x can never be 4 <g>.
Nov 27 2003
next sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bq6hkq$79q$1 digitaldaemon.com...
 "Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
 news:bq65gu$2n8o$1 digitaldaemon.com...
 Have to agree here, I rareley ( never ? ) use exceptions , and dont



 them being thrown without my knoweldge / consent.

Exactamundo, my friend. Exceptions are great for somethings, including actual serious/fatal

 or when doing stuff like deep-level parsing.

 Where they are absolutely not appropriate - and I can't believe this is

 a discusson - is a runtime report of a code-time mistake!!

There's no way to detect the error at compile time. What it's for is when one has: #1 switch (x) { case 1: ... case 2: ... case 3: ... } and then one day x has the value 4. In C, there's an implicit

 inserted when no default is explicitly supplied. But in my experience
 coding, debugging my own mistakes, and fixing other peoples' code, the
 implicit default break is almost always the WRONG thing to happen, and

 something unexpected happens as a result (i.e. crash, data corruption,
 etc.). When I see C code like that, and can ask the author, the intention

 that x will always be 1, 2 or 3 and NEVER anything else. That's why he

 the switch that way. That's ok, but then the maintenance programmer adds a
 feature where x is 4, updates all the switch statements accordingly, but
 inevitably misses one in the 100,000 line program he's revising. Even if

 original coder INTENDED to make use of the implicit default:break;, it has
 that distinct odor of a bug, and so I flag it in code reviews. And in my
 experience, it was never intended.

 For years, I've advocated 'defensive programming' in C by making it

 that the default case can never happen with:

 #2
     switch (x)
     {
         case 1: ...
         case 2: ...
         case 3: ...
         default:
             assert(0);
     }

 This is the normal practice in my own code. There are 3 situations to deal
 with, the above one, and:

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

 and:

 #4
     switch (x)
     {
         case 1: ...
         case 2: ...
         case 3: ...
         default:
             do something
             break;
     }

 What D does is make it easy to ensure that all the bases are covered by
 generating #2 if a default is not supplied. You're not going to get caught
 with a random crash from forgetting to deal with a case. The compiler

 do this at compile time because it has no way of determining all possible
 values x can take. In that way, it's similar to run time array bounds
 checking. The only other way of doing this with a hope of robustness is to
 *require* an explicitly written default statement for every switch. This
 would certainly be a valid language strategy, but I don't really want to

 nagged by the compiler to insert a default:assert(0); when I know darn

 that x can never be 4 <g>.

It's smacking me between the eyes that requiring the default is the sensible strategy. Original authoring costs are cheap; maintenance costs are expensive. You're just contradicting the bulk of this erudite argument by your knowingly weak convenience. Walter, if I can lose my bad habit of using leading underscores, I think you can endure this egregious constraint for the greater D good, no? :-)
Nov 27 2003
parent reply Roberto Mariottini <Roberto_member pathlink.com> writes:
In article <bq6kl9$bd6$1 digitaldaemon.com>, Matthew Wilson says...
"Walter" <walter digitalmars.com> wrote in message
news:bq6hkq$79q$1 digitaldaemon.com...

The only other way of doing this with a hope of robustness is to
 *require* an explicitly written default statement for every switch. This
 would certainly be a valid language strategy, but I don't really want to

 nagged by the compiler to insert a default:assert(0); when I know darn

 that x can never be 4 <g>.

It's smacking me between the eyes that requiring the default is the sensible strategy. Original authoring costs are cheap; maintenance costs are expensive. You're just contradicting the bulk of this erudite argument by your knowingly weak convenience.

I also support requiring default. The laziness is your biggest enemy, so type those 8 keys on the keyboard and that's all. But I know I'm almost alone here: people want to save typing, not to produce robust code. BTW, what about user defined enum? Can the compiler know whether all enum constants are used inside a switch, and signal to the programmer when some are missing and there is not a default? Ciao
Nov 28 2003
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Roberto Mariottini" <Roberto_member pathlink.com> wrote in message
news:bq7gm2$1nqo$1 digitaldaemon.com...
 BTW, what about user defined enum? Can the compiler know whether all enum
 constants are used inside a switch, and signal to the programmer when some

 missing and there is not a default?

Yes, but it would be presumptious for the compiler to assume that all values of an enum are possible.
Nov 28 2003
parent "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
 BTW, what about user defined enum? Can the compiler know whether all


 constants are used inside a switch, and signal to the programmer when


 are
 missing and there is not a default?

Yes, but it would be presumptious for the compiler to assume that all

 of an enum are possible.

Only if the default is there. Without the default, it would be appropriate. But wait! This is another reason to have the default ...
Nov 28 2003
prev sibling next sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
The only other way of doing this with a hope of robustness is to
 *require* an explicitly written default statement for every switch.



 would certainly be a valid language strategy, but I don't really want



be
 nagged by the compiler to insert a default:assert(0); when I know darn

 that x can never be 4 <g>.

It's smacking me between the eyes that requiring the default is the


strategy. Original authoring costs are cheap; maintenance costs are
expensive. You're just contradicting the bulk of this erudite argument by
your knowingly weak convenience.

I also support requiring default. The laziness is your biggest enemy, so type those 8 keys on the keyboard and that's all. But I know I'm almost

 here: people want to save typing, not to produce robust code.

Well said. Alas, what you say seems to be true a lot of the time. Even with big W ... :(
Nov 28 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bq82ts$2jce$1 digitaldaemon.com...
 I also support requiring default. The laziness is your biggest enemy,
 so type those 8 keys on the keyboard and that's all. But I know I'm


 alone
 here: people want to save typing, not to produce robust code.

Well said. Alas, what you say seems to be true a lot of the time. Even with big W ... :(

Why is providing a runtime check that all the bases were covered make a program less robust? What really is the essential difference between requiring that the programmer explicitly code a default, and requiring him to explicitly check array bounds: if (0 <= i && i < array.length) j = array[i]; ?
Nov 28 2003
parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bq8adb$2u45$2 digitaldaemon.com...
 "Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
 news:bq82ts$2jce$1 digitaldaemon.com...
 I also support requiring default. The laziness is your biggest enemy,
 so type those 8 keys on the keyboard and that's all. But I know I'm


 alone
 here: people want to save typing, not to produce robust code.

Well said. Alas, what you say seems to be true a lot of the time. Even with big W


 :(

Why is providing a runtime check that all the bases were covered make a program less robust?

Anything that pushes out error-detection from (where it is achievable, of course) compile-time to runtime loses robustness. Isn't that axiomatic? If that's not the case, then why don't we all program in Python all the time? It'd be a damn sight easier.
 What really is the essential difference between
 requiring that the programmer explicitly code a default, and requiring him
 to explicitly check array bounds:

     if (0 <= i && i < array.length) j = array[i];

 ?

This is not a valid analogy. Your example is the difference between checking values yourself, or having them checked at runtime. With the switch/default, it is the case of having the options checked at compile time (by requiring all members of an enum to be present), or explicitly telling the compiler that you are aware of/don't care about the default case (with missing enums, or with other types) vs not being "complete" in the code and letting the runtime handle it. They are quite different things.
Nov 28 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bq8cg7$30ro$1 digitaldaemon.com...
 Why is providing a runtime check that all the bases were covered make a
 program less robust?

course) compile-time to runtime loses robustness. Isn't that axiomatic?

It's not achievable at compile time, regardless of whether the default is inserted by the compiler or the programmer.
 What really is the essential difference between
 requiring that the programmer explicitly code a default, and requiring


 to explicitly check array bounds:

     if (0 <= i && i < array.length) j = array[i];

 ?

This is not a valid analogy. Your example is the difference between

 values yourself, or having them checked at runtime. With the

 it is the case of having the options checked at compile time (by requiring
 all members of an enum to be present), or explicitly telling the compiler
 that you are aware of/don't care about the default case (with missing

 or with other types) vs not being "complete" in the code and letting the
 runtime handle it. They are quite different things.

Yes, but putting a 'default:assert(0);' in is a runtime check, regardless of whether it is coded explicitly or implicitly. Therefore, it is just like an array bounds check.
Dec 02 2003
parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bqitol$2va4$1 digitaldaemon.com...
 "Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
 news:bq8cg7$30ro$1 digitaldaemon.com...
 Why is providing a runtime check that all the bases were covered make



 program less robust?



 course) compile-time to runtime loses robustness. Isn't that axiomatic?

It's not achievable at compile time, regardless of whether the default is inserted by the compiler or the programmer.

Why not?
Dec 02 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bqjqtl$18hg$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:bqitol$2va4$1 digitaldaemon.com...
 "Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
 news:bq8cg7$30ro$1 digitaldaemon.com...
 Why is providing a runtime check that all the bases were covered




 a
 program less robust?



 course) compile-time to runtime loses robustness. Isn't that



 It's not achievable at compile time, regardless of whether the default


 inserted by the compiler or the programmer.

Why not?

Casting, unions, pointers, reading data from files, inline assembler, external functions, buffer overflows overwriting the stack with a virus(!), etc. All kinds of ways to have random bit patterns in an otherwise constrained repository for a type. Just the kinds of problems DbC should help catch <g>.
Dec 08 2003
parent reply "Julio César Carrascal Urquijo" <adnoctum phreaker.net> writes:
"Walter" <walter digitalmars.com> wrote in message
news:br3s0v$19rs$2 digitaldaemon.com...

 Casting, unions, pointers, reading data from files, inline assembler,
 external functions, buffer overflows overwriting the stack with a

 etc. All kinds of ways to have random bit patterns in an otherwise
 constrained repository for a type. Just the kinds of problems DbC should
 help catch <g>.

I though this will throw an IllegalCastException or something like that but it doesn't: enum SomeEnum {cero, uno, dos, tres}; void main() { SomeEnum se = cast(SomeEnum)10; printf("%d\n", se); } Shouldn't the cast line had a runtime check? Just asking.
Dec 09 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Julio César Carrascal Urquijo" <adnoctum phreaker.net> wrote in message
news:br72i4$9f2$1 digitaldaemon.com...
 Shouldn't the cast line had a runtime check? Just asking.

I could argue it both ways <g>.
Dec 18 2003
parent reply "Julio César Carrascal Urquijo" <adnoctum phreaker.net> writes:
Walter <walter digitalmars.com> escribió en el mensaje de noticias
brtenf$1uqp$1 digitaldaemon.com...
 "Julio César Carrascal Urquijo" <adnoctum phreaker.net> wrote in message
 news:br72i4$9f2$1 digitaldaemon.com...
 Shouldn't the cast line had a runtime check? Just asking.

I could argue it both ways <g>.

At least in debug builds, please! (Just like array bounds checking)
Dec 18 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Julio César Carrascal Urquijo" <adnoctum phreaker.net> wrote in message
news:brtn8a$2blv$1 digitaldaemon.com...
 Walter <walter digitalmars.com> escribió en el mensaje de noticias
 brtenf$1uqp$1 digitaldaemon.com...
 "Julio César Carrascal Urquijo" <adnoctum phreaker.net> wrote in message
 news:br72i4$9f2$1 digitaldaemon.com...
 Shouldn't the cast line had a runtime check? Just asking.

I could argue it both ways <g>.

At least in debug builds, please! (Just like array bounds checking)

It's a good idea, but I have to redo the template stuff first.
Dec 19 2003
next sibling parent reply "Sean L. Palmer" <palmer.sean verizon.net> writes:
Shall we reopen the discussion, or do you already know what you are going to
do?

Sean

"Walter" <walter digitalmars.com> wrote in message
news:brv8kk$1ldt$1 digitaldaemon.com...
 It's a good idea, but I have to redo the template stuff first.

Dec 19 2003
parent "Walter" <walter digitalmars.com> writes:
I think I know what to do.

"Sean L. Palmer" <palmer.sean verizon.net> wrote in message
news:brvl0p$28qn$1 digitaldaemon.com...
 Shall we reopen the discussion, or do you already know what you are going

 do?

 Sean

 "Walter" <walter digitalmars.com> wrote in message
 news:brv8kk$1ldt$1 digitaldaemon.com...
 It's a good idea, but I have to redo the template stuff first.


Dec 19 2003
prev sibling parent "Julio César Carrascal Urquijo" <adnoctum phreaker.net> writes:
Walter <walter digitalmars.com> escribió en el mensaje de noticias
brv8kk$1ldt$1 digitaldaemon.com...

(...)
 It's a good idea, but I have to redo the template stuff first.

Ok, thanks!
Dec 19 2003
prev sibling parent reply Ilya Minkov <minkov cs.tum.edu> writes:
Roberto Mariottini wrote:
 I also support requiring default. The laziness is your biggest enemy,
 so type those 8 keys on the keyboard and that's all. But I know I'm almost
alone
 here: people want to save typing, not to produce robust code.

Requiring default will bring us to people typing "default: break;" instead of "default: assert(0)" just exactly for the reason you stated!
 BTW, what about user defined enum? Can the compiler know whether all enum
 constants are used inside a switch, and signal to the programmer when some are
 missing and there is not a default?

Cool idea! -eye
Nov 29 2003
next sibling parent Berin Loritsch <bloritsch d-haven.org> writes:
Ilya Minkov wrote:

 Roberto Mariottini wrote:
 
 I also support requiring default. The laziness is your biggest enemy,
 so type those 8 keys on the keyboard and that's all. But I know I'm 
 almost alone
 here: people want to save typing, not to produce robust code.

Requiring default will bring us to people typing "default: break;" instead of "default: assert(0)" just exactly for the reason you stated!

For some people, that is exactly what they want. You do need to walk a balance between *encouraging* good coding practice, and *requiring* it.
Dec 01 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Ilya Minkov" <minkov cs.tum.edu> wrote in message
news:bqaci5$2rl3$2 digitaldaemon.com...
 Requiring default will bring us to people typing "default: break;"
 instead of "default: assert(0)"  just exactly for the reason you stated!

A similar problem has happened with Java where it required you to list the possible exceptions generated by each function. Even expert programmers who publicly excoriated the practice would pepper their own code with generic catches just to shut up the compiler. The end result was that a rule that was supposed to increase robustness, actually wound up making things worse. Philosophically, I don't think a language is robust because it requires programmers to code in a certain way. I think it's robust if it allows programmers to program the way they want to, making robust programming practices easier to use than non-robust ones. For example, although D allows you to use pointers as you would in C, D provides better semantic alternatives (like out parameters) that are more robust, and easier to use than the pointer equivalents. It's a win-win. One can counter this by saying "why do static type checking at all, then? Why not do it at runtime?" It's an excellent point, and the answer is that it's just too expensive to do at runtime. Languages that rely on runtime type checking tend to run very slowly compared with statically typed languages. As always, everything is a compromise in programming, and so we have static type checking in D.
Dec 02 2003
next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Walter wrote:
 One can counter this by saying "why do static type checking at all, then?
 Why not do it at runtime?" It's an excellent point, and the answer is that
 it's just too expensive to do at runtime. Languages that rely on runtime
 type checking tend to run very slowly compared with statically typed
 languages. As always, everything is a compromise in programming, and so we
 have static type checking in D.

You'd prefer runtime type checking to compile time type checking? Urrrgh! I think we are very different types of programmers... I prefer the compiler to tell me about as many bugs as possible while I can still do something about it (at compile time). If all the checks happen at runtime, chances are that some of these bugs will not be discovered until it's too late - i.e. when the program is shipped and a customer has run the program in a way that triggered some condition that was not tested. Hauke
Dec 02 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
news:bqj2ca$4lq$1 digitaldaemon.com...
 Walter wrote:
 One can counter this by saying "why do static type checking at all,


 Why not do it at runtime?" It's an excellent point, and the answer is


 it's just too expensive to do at runtime. Languages that rely on runtime
 type checking tend to run very slowly compared with statically typed
 languages. As always, everything is a compromise in programming, and so


 have static type checking in D.

Urrrgh! I think we are very different types of programmers...

No, that's not what I meant. Certainly, the earlier errors can be detected, the better, and compile time is the earliest. The compiler should do as much as it can, and runtime checks inserted to cover what it cannot.
 I prefer the compiler to tell me about as many bugs as possible while I
 can still do something about it (at compile time).

Yes, you are correct.
 If all the checks
 happen at runtime, chances are that some of these bugs will not be
 discovered until it's too late - i.e. when the program is shipped and a
 customer has run the program in a way that triggered some condition that
 was not tested.

You're right. But I'd also prefer a runtime that generated an exception on an untested unanticipated condition, rather than fall through to some essentially random default behavior, which is what happens with C's implicit switch default behavior. It's very, very important that programs fail in a controlled manner when they encounter unanticipated conditions.
Dec 02 2003
next sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bqjanc$gfp$1 digitaldaemon.com...
 "Hauke Duden" <H.NS.Duden gmx.net> wrote in message
 news:bqj2ca$4lq$1 digitaldaemon.com...
 Walter wrote:
 One can counter this by saying "why do static type checking at all,


 Why not do it at runtime?" It's an excellent point, and the answer is


 it's just too expensive to do at runtime. Languages that rely on



 type checking tend to run very slowly compared with statically typed
 languages. As always, everything is a compromise in programming, and



 we
 have static type checking in D.

Urrrgh! I think we are very different types of programmers...

No, that's not what I meant. Certainly, the earlier errors can be

 the better, and compile time is the earliest. The compiler should do as

 as it can, and runtime checks inserted to cover what it cannot.

 I prefer the compiler to tell me about as many bugs as possible while I
 can still do something about it (at compile time).

Yes, you are correct.
 If all the checks
 happen at runtime, chances are that some of these bugs will not be
 discovered until it's too late - i.e. when the program is shipped and a
 customer has run the program in a way that triggered some condition that
 was not tested.

You're right. But I'd also prefer a runtime that generated an exception on an untested unanticipated condition, rather than fall through to some essentially random default behavior, which is what happens with C's

 switch default behavior.

 It's very, very important that programs fail in a controlled manner when
 they encounter unanticipated conditions.

Ok, I can (hopefully) see where you're not understanding our argument. AFAICT no-one's arguing for a C status-quo, which is what all your arguments seem to be addressing. Put in its simplest form: we want the compiler to force us to write a default, *in all cases*. My previous suggestion was to enable the syntactic sugar of substituting "unexpected:" for "default: assert(0);". (Note that "unexpected:" would throw something a little more informative than assert(0), perhaps an UnexpectedCaseException?)
Dec 02 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bqjr7a$197c$1 digitaldaemon.com...
 Ok, I can (hopefully) see where you're not understanding our argument.
 AFAICT no-one's arguing for a C status-quo, which is what all your

 seem to be addressing.

So, we all agree that C's implicit default:break; is not what we want? (This may cause some C code ported to D to have problems crop up. I think this is bearable, I just want to make sure it is known.) Does this also mean we agree that throwing a runtime exception on the default is not a bad thing if it is intended that cases cover all the bases?
 Put in its simplest form: we want the compiler to
 force us to write a default, *in all cases*.

If this is our only point of disagreement on this, I don't think it's worthy of all the heat!
 My previous suggestion was to
 enable the syntactic sugar of substituting "unexpected:" for "default:
 assert(0);". (Note that "unexpected:" would throw something a little more
 informative than assert(0), perhaps an UnexpectedCaseException?)

Actually, it throws a SwitchError exception, giving file name and line number.
Dec 08 2003
next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Walter wrote:
Ok, I can (hopefully) see where you're not understanding our argument.
AFAICT no-one's arguing for a C status-quo, which is what all your

arguments
seem to be addressing.

So, we all agree that C's implicit default:break; is not what we want? (This may cause some C code ported to D to have problems crop up. I think this is bearable, I just want to make sure it is known.) Does this also mean we agree that throwing a runtime exception on the default is not a bad thing if it is intended that cases cover all the bases?

I hate to make this more complicated, but ... I very much prefer the default;break; to the default;throw. Sorry! ;) I could settle for an explicit default requirement, but I don't like the exception at all. Here's why: From another one of your posts on this topic: Walter wrote:
switch (value)
 {
     case 1:
         blah();
         break;
     case 2:
         foo();
         break;
     case 3:
         bar();
         break;
 }

values for value are 1, 2 or 3. No other values are possible. Therefore, it is a program bug if there are any other values."

I don't! Not at all. I believe that's a difference in our coding styles, or maybe the kind of applications we write, but I often use switch in cases where I want the function to do a task X in all cases, but in some cases do tasks A,B or C before that. That translates to a switch statement of the form: switch(bla) { case 1: doA(); break; case 2: doB(); break; case 3: doC(); break; [implicit default;break = do nothing for all other cases] } //always executed. doX(); I performed a search on some of my code and I found that switch statements where I really handle ALL cases are pretty rare. Especially in my networking-related applications there are a lot more switches of the kind I described above. Compiler software may be different, because, for example, you want to output an error if there is some unexpected token - but this doesn't apply to all kinds of software. You said you didn't want to force a particular coding style, so please don't force the way switch has to be used! Hauke
Dec 09 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
news:br4r11$2tdu$1 digitaldaemon.com...
 I hate to make this more complicated, but ... I very much prefer the
 default;break; to the default;throw. Sorry! ;)

 I could settle for an explicit default requirement, but I don't like the
   exception at all. Here's why:

  From another one of your posts on this topic:

 Walter wrote:
  >switch (value)
  >> {
  >>     case 1:
  >>         blah();
  >>         break;
  >>     case 2:
  >>         foo();
  >>         break;
  >>     case 3:
  >>         bar();
  >>         break;
  >> }
  >I view the above switch statement as explicitly saying "the only
possible
  >values for value are 1, 2 or 3. No other values are possible.
  >Therefore, it
  >is a program bug if there are any other values."

 I don't! Not at all. I believe that's a difference in our coding styles,

Yes, it's a clear difference in our styles of using switch statements. My experience in code reviews, however, is when there is no explicit default in the switch and I question the programmer about it, it's almost always an oversight (i.e. a bug).
 I performed a search on some of my code and I found that switch
 statements where I really handle ALL cases are pretty rare.

Mine too, but I like to put in a default:break; to signal to anyone reading the code that I didn't just overlook the other cases!
 You said you didn't want to force a particular coding style, so please
 don't force the way switch has to be used!

I wish I could please everyone on this, but it's not possible.
Dec 09 2003
next sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
"Walter" <walter digitalmars.com> wrote in message
news:br53qk$98m$1 digitaldaemon.com...
 "Hauke Duden" <H.NS.Duden gmx.net> wrote in message
 news:br4r11$2tdu$1 digitaldaemon.com...
 I hate to make this more complicated, but ... I very much prefer the
 default;break; to the default;throw. Sorry! ;)

 I could settle for an explicit default requirement, but I don't like the
   exception at all. Here's why:

  From another one of your posts on this topic:

 Walter wrote:
  >switch (value)
  >> {
  >>     case 1:
  >>         blah();
  >>         break;
  >>     case 2:
  >>         foo();
  >>         break;
  >>     case 3:
  >>         bar();
  >>         break;
  >> }
  >I view the above switch statement as explicitly saying "the only
possible
  >values for value are 1, 2 or 3. No other values are possible.
  >Therefore, it
  >is a program bug if there are any other values."

 I don't! Not at all. I believe that's a difference in our coding styles,

Yes, it's a clear difference in our styles of using switch statements. My experience in code reviews, however, is when there is no explicit default

 the switch and I question the programmer about it, it's almost always an
 oversight (i.e. a bug).

So they should have put in a default, so the compiler should make them do so. QED. You just keep proving our point.
Dec 09 2003
parent reply Lewis Miller <Lewis_member pathlink.com> writes:
This probably wont help any but a switch that acts like visual basics Select
Case statement would be the my preferred method.
Select Case Acts like so:

Select Case Foo

Case 1
DoSomething()

Case 2,3,4,5
DoSomething()

Case 10
DoSomething()

Case Else
Nothing Was Caught So Do A Default

End Select

What happens, is that it falls down thru till a match is found, an that matching
statement is then executed... if nothing is found, the Case Else is excecuted.
The Case Else is optional, so if nothing is matched, nothing is excecuted.
Dec 09 2003
next sibling parent reply J C Calvarese <jcc7 cox.net> writes:
Lewis Miller wrote:

 This probably wont help any but a switch that acts like visual basics Select
 Case statement would be the my preferred method.
 Select Case Acts like so:
 
 Select Case Foo
 
 Case 1
 DoSomething()
 
 Case 2,3,4,5
 DoSomething()
 
 Case 10
 DoSomething()
 
 Case Else
 Nothing Was Caught So Do A Default
 
 End Select
 
 What happens, is that it falls down thru till a match is found, an that
matching
 statement is then executed... if nothing is found, the Case Else is excecuted.
 The Case Else is optional, so if nothing is matched, nothing is excecuted.
 

experience in VB/VBA.) It handles the cases I ask it to handle. That's enough for me. Justin
Dec 09 2003
parent Lewis <dethbomb hotmail.com> writes:
J C Calvarese wrote:
 Lewis Miller wrote:
 
 This probably wont help any but a switch that acts like visual basics 
 Select
 Case statement would be the my preferred method.
 Select Case Acts like so:

 Select Case Foo

 Case 1
 DoSomething()

 Case 2,3,4,5
 DoSomething()

 Case 10
 DoSomething()

 Case Else
 Nothing Was Caught So Do A Default

 End Select

 What happens, is that it falls down thru till a match is found, an 
 that matching
 statement is then executed... if nothing is found, the Case Else is 
 excecuted.
 The Case Else is optional, so if nothing is matched, nothing is 
 excecuted.

experience in VB/VBA.) It handles the cases I ask it to handle. That's enough for me. Justin

interestingly enough, vb also has a switch statement i.e i = Switch( Index , 1, 2, 3, 4, 5) Following that, i would say that it would be best to keep switch close to what it is, and add a new keyword (such as branch() i like) for a advanced switch statement
Dec 09 2003
prev sibling next sibling parent reply Dan Liebgold <Dan_member pathlink.com> writes:
In article <br5b9g$l4b$1 digitaldaemon.com>, Lewis Miller says...
This probably wont help any but a switch that acts like visual basics Select
Case statement would be the my preferred method.
Select Case Acts like so:

Select Case Foo

Case 1
DoSomething()

Case 2,3,4,5
DoSomething()

Case 10
DoSomething()

Case Else
Nothing Was Caught So Do A Default

End Select

What happens, is that it falls down thru till a match is found, an that matching
statement is then executed... if nothing is found, the Case Else is excecuted.
The Case Else is optional, so if nothing is matched, nothing is excecuted.

I must agree with this approach. This, to my mind, is the right way (TM) to do switches/cases/selects. The C'ish approach smacks of hackery and danger (dangery? hackanger?). I've been working in a language other than C/C++ for ~2 years now professionally (game development, native code compiled language), and while it does resemble C++ in many ways, it implements switches as above. I must say I don't miss C's questionable switches *at all*. They seem quite bad to my mind, and I'm glad to have moved away from them. (hmm... what's this dead horse doing here?) Fall-through is bad! You never *ever* need it. If you reexamine all your switch statements in your own code and think of how they'd be done if fall-through were not allowed, I believe you will find that you could write clearer code once you've made the paradigm shift. Fall-through's are treacherous short-cuts and are bugs waiting to happen. They do not belong in a language that is attempting to increase code safety. The reuse of the 'break' keyword is confusing at best, and significantly dangerous at worse (see http://www.cs.berkeley.edu/~nikitab/courses/cs294-8/hw1.html for an example where a programmer expected the 'break' in a 'switch' to break out of an enclosing loop, causing a telephone system failure that ran up a $60 million loss). 'Break' should mean only one thing! Switch statements cases should break implicitly. Cases should be stackable as above to account for handling multiple cases exactly the same way. Goto's, explicit fall-throughs, and other sleights of hand are extra. C# is blazing the way, we must merely follow. (I bet that dead horse will think twice next time!) As for requiring a 'default' case, I think that is an extreme response to a specific problem. It really looks no different (to my eyes) than requiring an 'else' for every 'if'. Throwing an exception when a case isn't handled *will* force programmers to write safer code in general, but the cost is not insignificant. I can see both sides of this issue so I'll defer to Walter for the force of the argument. There, I've had my rant :) Dan L.
Dec 09 2003
parent Felix <Felix_member pathlink.com> writes:
Me too, I do not like that "break"'s in the switch.
But I understand the others.
Maybe Walter should add also a "branch" statement, where the rules are similar
to Pascal's "case": no breaks (or the break should be implicit), "ranged" cases
(i.e. case 1..8) etc. To meet all tastes...



In article <br5ni9$189d$1 digitaldaemon.com>, Dan Liebgold says...
In article <br5b9g$l4b$1 digitaldaemon.com>, Lewis Miller says...
This probably wont help any but a switch that acts like visual basics Select
Case statement would be the my preferred method.
Select Case Acts like so:

Select Case Foo

Case 1
DoSomething()

Case 2,3,4,5
DoSomething()

Case 10
DoSomething()

Case Else
Nothing Was Caught So Do A Default

End Select

What happens, is that it falls down thru till a match is found, an that matching
statement is then executed... if nothing is found, the Case Else is excecuted.
The Case Else is optional, so if nothing is matched, nothing is excecuted.

I must agree with this approach. This, to my mind, is the right way (TM) to do switches/cases/selects. The C'ish approach smacks of hackery and danger (dangery? hackanger?). I've been working in a language other than C/C++ for ~2 years now professionally (game development, native code compiled language), and while it does resemble C++ in many ways, it implements switches as above. I must say I don't miss C's questionable switches *at all*. They seem quite bad to my mind, and I'm glad to have moved away from them. (hmm... what's this dead horse doing here?) Fall-through is bad! You never *ever* need it. If you reexamine all your switch statements in your own code and think of how they'd be done if fall-through were not allowed, I believe you will find that you could write clearer code once you've made the paradigm shift. Fall-through's are treacherous short-cuts and are bugs waiting to happen. They do not belong in a language that is attempting to increase code safety. The reuse of the 'break' keyword is confusing at best, and significantly dangerous at worse (see http://www.cs.berkeley.edu/~nikitab/courses/cs294-8/hw1.html for an example where a programmer expected the 'break' in a 'switch' to break out of an enclosing loop, causing a telephone system failure that ran up a $60 million loss). 'Break' should mean only one thing! Switch statements cases should break implicitly. Cases should be stackable as above to account for handling multiple cases exactly the same way. Goto's, explicit fall-throughs, and other sleights of hand are extra. C# is blazing the way, we must merely follow. (I bet that dead horse will think twice next time!) As for requiring a 'default' case, I think that is an extreme response to a specific problem. It really looks no different (to my eyes) than requiring an 'else' for every 'if'. Throwing an exception when a case isn't handled *will* force programmers to write safer code in general, but the cost is not insignificant. I can see both sides of this issue so I'll defer to Walter for the force of the argument. There, I've had my rant :) Dan L.

Dec 09 2003
prev sibling parent J Anderson <REMOVEanderson badmama.com.au> writes:
Lewis Miller wrote:

This probably wont help any but a switch that acts like visual basics Select
Case statement would be the my preferred method.
Select Case Acts like so:

Select Case Foo

Case 1
DoSomething()

Case 2,3,4,5
DoSomething()

Case 10
DoSomething()

Case Else
Nothing Was Caught So Do A Default

End Select

What happens, is that it falls down thru till a match is found, an that matching
statement is then executed... if nothing is found, the Case Else is excecuted.
The Case Else is optional, so if nothing is matched, nothing is excecuted.

  

slightly different, but it's the general idea that counts.
Dec 09 2003
prev sibling parent Georg Wrede <Georg_member pathlink.com> writes:
In a recent post Walter wrote:
 
 While I generally agree with you here, the point I make is
 that the language should make the right thing to do the
 easiest thing to do.
 

 
 Mine too, but I like to put in a default:break; to signal to
 anyone reading the code that I didn't just overlook the
 other cases!

These statements combined would seem to vote for an obligatory explicit default in switch statements? I think most of the time people don't put a default in their switch 'cause they forgot. The compiler could "remind" them. If one is stupid enough to put a default:break when they shouldn't, then it's their own fault. But most people would just remember that this was what they forgot, no biggie. Incidentally, I was one who got derailed with the assert(0) thing. That sounded really unfriendly. Throwing a proper error is much better, although I still vote for an obligatory and explicit default.
Dec 10 2003
prev sibling parent reply The Lone Haranguer <The_member pathlink.com> writes:
C has an implicit default:break; ?

Regardless, as I don't like your built-in assert(), I certainly don't want the
compiler to add one.

Why do you insist on not breaking ported C code in some cases, while going out
of your way to break it in other cases? Make up your mind!

In article <br3sjr$1ajv$2 digitaldaemon.com>, Walter says...
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bqjr7a$197c$1 digitaldaemon.com...
 Ok, I can (hopefully) see where you're not understanding our argument.
 AFAICT no-one's arguing for a C status-quo, which is what all your

 seem to be addressing.

So, we all agree that C's implicit default:break; is not what we want? (This may cause some C code ported to D to have problems crop up. I think this is bearable, I just want to make sure it is known.) Does this also mean we agree that throwing a runtime exception on the default is not a bad thing if it is intended that cases cover all the bases?
 Put in its simplest form: we want the compiler to
 force us to write a default, *in all cases*.

If this is our only point of disagreement on this, I don't think it's worthy of all the heat!
 My previous suggestion was to
 enable the syntactic sugar of substituting "unexpected:" for "default:
 assert(0);". (Note that "unexpected:" would throw something a little more
 informative than assert(0), perhaps an UnexpectedCaseException?)

Actually, it throws a SwitchError exception, giving file name and line number.

Dec 09 2003
next sibling parent "Walter" <walter digitalmars.com> writes:
"The Lone Haranguer" <The_member pathlink.com> wrote in message
news:br58dg$gms$1 digitaldaemon.com...
 C has an implicit default:break; ?

Yes.
 Regardless, as I don't like your built-in assert(), I certainly don't want

 compiler to add one.

 Why do you insist on not breaking ported C code in some cases, while going

 of your way to break it in other cases? Make up your mind!

I plead guilty to the inconsistency.
Dec 09 2003
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I think that you are missing one of the central tenets of Walter's 
design of D.  Quoted from http://digitalmars.com/d/overview.html, the 
First Major Goal of D is:

<quote>
Reduce software development costs by at least 10% by adding in proven 
productivity enhancing features and by adjusting language features so 
that common, time-consuming bugs are eliminated from the start.
</quote>

So, as I have seen over and over, Walter's intention is to design a 
language which makes it easy to do the Right Thing and hard(er) to do 
the Wrong Thing.  He has made the language very C-like so that it is 
easy to port, but intentionally breaks things that he believes led to 
lots of bugs in C.

In this discussion, we have seen a wide variety of opinions about what 
the Right Thing is; thus, we have had a lot of debate.  I think it's 
fair to discuss with Walter about what the Right Thing is, but I don't 
think it's fair to call him inconsistent.  C compatibility was never a 
stated goal.

Hang in there, Walter!  The current storm will subside.

Russ

The Lone Haranguer wrote:
 C has an implicit default:break; ?
 
 Regardless, as I don't like your built-in assert(), I certainly don't want the
 compiler to add one.
 
 Why do you insist on not breaking ported C code in some cases, while going out
 of your way to break it in other cases? Make up your mind!
 
 In article <br3sjr$1ajv$2 digitaldaemon.com>, Walter says...
 
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bqjr7a$197c$1 digitaldaemon.com...

Ok, I can (hopefully) see where you're not understanding our argument.
AFAICT no-one's arguing for a C status-quo, which is what all your

arguments
seem to be addressing.

So, we all agree that C's implicit default:break; is not what we want? (This may cause some C code ported to D to have problems crop up. I think this is bearable, I just want to make sure it is known.) Does this also mean we agree that throwing a runtime exception on the default is not a bad thing if it is intended that cases cover all the bases?
Put in its simplest form: we want the compiler to
force us to write a default, *in all cases*.

If this is our only point of disagreement on this, I don't think it's worthy of all the heat!
My previous suggestion was to
enable the syntactic sugar of substituting "unexpected:" for "default:
assert(0);". (Note that "unexpected:" would throw something a little more
informative than assert(0), perhaps an UnexpectedCaseException?)

Actually, it throws a SwitchError exception, giving file name and line number.


Dec 10 2003
next sibling parent "Walter" <walter digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:br7rue$1gaa$2 digitaldaemon.com...
 Hang in there, Walter!  The current storm will subside.

Thanks! At the end of the day, I just have to make a decision and stick with it, or D will never move forward.
Dec 10 2003
prev sibling next sibling parent The Lone Haranguer <The_member pathlink.com> writes:
Well, based on both those arguments, fall-through should be abandoned. I
understood fall-through was left in for C compatibility, and it leads to many
bugs.

In article <br7rue$1gaa$2 digitaldaemon.com>, Russ Lewis says...
I think that you are missing one of the central tenets of Walter's 
design of D.  Quoted from http://digitalmars.com/d/overview.html, the 
First Major Goal of D is:

<quote>
Reduce software development costs by at least 10% by adding in proven 
productivity enhancing features and by adjusting language features so 
that common, time-consuming bugs are eliminated from the start.
</quote>

So, as I have seen over and over, Walter's intention is to design a 
language which makes it easy to do the Right Thing and hard(er) to do 
the Wrong Thing.  He has made the language very C-like so that it is 
easy to port, but intentionally breaks things that he believes led to 
lots of bugs in C.

In this discussion, we have seen a wide variety of opinions about what 
the Right Thing is; thus, we have had a lot of debate.  I think it's 
fair to discuss with Walter about what the Right Thing is, but I don't 
think it's fair to call him inconsistent.  C compatibility was never a 
stated goal.

Hang in there, Walter!  The current storm will subside.

Russ

Dec 10 2003
prev sibling parent The Lone Haranguer <The_member pathlink.com> writes:
I think perhaps this could be used as an argument for strongly-typed booleans
too.
The C practice of:
int x = something ;
if ( x ) ... ;
or worse:
char* s = something ;
if ( s ) ... ;
is at least poor style, and can lead to higher maintenance time and cost (due to
maintenance being done by programmers less-experienced than the original
programmers (who didn't write any comments because they feel that the code is
the documentation)).

<On my last job I had to deal with a lot of the latter example and always
replaced them with  if ( s != NULL ) ... ; >

So, although it may not lead to bugs, it's the "Wrong Thing". And strongly-typed
booleans should be the fix.

And it seems the only reason to continue to allow it is because of portability
with C.

And furthermore, as mentioned before, strongly-typed booleans also solve the
problem of when someone writes  if ( x = y ) ... ;  accidently. (Unless x and y
are boolean, but how often would you do that?)

Oh, sheesh, I'm still writing? Well let me get it over with here and not have to
write another message.
As mentioned before, yet another solution to the "assign instead of equality"
mistake is to have a different assignment operator, e.g. := . At one point
Walter said that makes sense. Again, it seems that the only reason to keep the
"C assignment operator" is for portability!


In article <br7rue$1gaa$2 digitaldaemon.com>, Russ Lewis says...
I think that you are missing one of the central tenets of Walter's 
design of D.  Quoted from http://digitalmars.com/d/overview.html, the 
First Major Goal of D is:

<quote>
Reduce software development costs by at least 10% by adding in proven 
productivity enhancing features and by adjusting language features so 
that common, time-consuming bugs are eliminated from the start.
</quote>

So, as I have seen over and over, Walter's intention is to design a 
language which makes it easy to do the Right Thing and hard(er) to do 
the Wrong Thing.  He has made the language very C-like so that it is 
easy to port, but intentionally breaks things that he believes led to 
lots of bugs in C.

In this discussion, we have seen a wide variety of opinions about what 
the Right Thing is; thus, we have had a lot of debate.  I think it's 
fair to discuss with Walter about what the Right Thing is, but I don't 
think it's fair to call him inconsistent.  C compatibility was never a 
stated goal.

Hang in there, Walter!  The current storm will subside.

Russ

Dec 10 2003
prev sibling next sibling parent reply Andy Friesen <andy ikagames.com> writes:
Walter wrote:
 It's very, very important that programs fail in a controlled manner when
 they encounter unanticipated conditions.

Nondeterministic bugs are great for burning a few hours (and brain cells) -- andy
Dec 02 2003
parent "Walter" <walter digitalmars.com> writes:
"Andy Friesen" <andy ikagames.com> wrote in message
news:bqk3hl$1ldp$1 digitaldaemon.com...
 Walter wrote:
 It's very, very important that programs fail in a controlled manner when
 they encounter unanticipated conditions.


Yup. That's why DbC is so valuable, as the earlier in the program the bug is detected the more likely it is deterministic.
Dec 08 2003
prev sibling parent reply "Lars Ivar Igesund" <larsivar igesund.net> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bqjanc$gfp$1 digitaldaemon.com...
 It's very, very important that programs fail in a controlled manner when
 they encounter unanticipated conditions.

I might accept your solution if you make a minor change. Don't use assert(0);! As a programmer I might sprinkle asserts in my code and when I do, I know that some of my code (or at least the user of my code) has made a mistake and I might start to look for the assert in question. But suddenly I get an assert exception that don't match an assert in my code! Can be nothing else than an compiler bug. And I suppose I will flag it as such if it ever happens to me. The exception thrown MUST be a well documented and well specified exception (like the UnknownSwitchCaseException suggested by Matthew). Even better, demand the default case at compile time. Then it will never be a problem at runtime! Lars Ivar Igesund
Dec 03 2003
next sibling parent reply Brad Beveridge <brad clear.net.nz> writes:
I may be not getting this argument.
I see Water saying:
You can have a default case if you want to, it may do nothing.
The normal default case will throw an exception.

Others are saying:
Let the default case be do nothing (like C/C++).  And it's up to the 
programmer to explicity decide if the default case is an error.

Personally I like the default exception method, it will catch bugs.  But 
I can see how porting legacy code directly from C would possibly allow 
different kinds of bugs to slip through.
Why not have a compiler switch that can enforce the requirement for a 
default case - then everyone wins.  Those that like the fall through 
behaviour will get compile time errors & be forced to place empty 
default cases, those that like the default exception method can have 
that too.
At the end of the day it's a programmer preference.

Brad

Lars Ivar Igesund wrote:
 "Walter" <walter digitalmars.com> wrote in message
 news:bqjanc$gfp$1 digitaldaemon.com...
 
It's very, very important that programs fail in a controlled manner when
they encounter unanticipated conditions.

I might accept your solution if you make a minor change. Don't use assert(0);! As a programmer I might sprinkle asserts in my code and when I do, I know that some of my code (or at least the user of my code) has made a mistake and I might start to look for the assert in question. But suddenly I get an assert exception that don't match an assert in my code! Can be nothing else than an compiler bug. And I suppose I will flag it as such if it ever happens to me. The exception thrown MUST be a well documented and well specified exception (like the UnknownSwitchCaseException suggested by Matthew). Even better, demand the default case at compile time. Then it will never be a problem at runtime! Lars Ivar Igesund

lid Date
parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
 I may be not getting this argument.
 I see Water saying:
 You can have a default case if you want to, it may do nothing.
 The normal default case will throw an exception.

 Others are saying:
 Let the default case be do nothing (like C/C++).  And it's up to the
 programmer to explicity decide if the default case is an error.

That is not so. We're saying that the default must be specified. The logic is simple: 1. Omitting "default" can be problematic 2. Having default behaviour, that throws an exception, when "default" is ommitted can be problematic 3. Requiring default (or unexpected) be specified costs nothing but fewer maintenance keystrokes. As the yanks like to say: you do the math!
 Personally I like the default exception method, it will catch bugs.

This is wrong. It will catch _some_ bugs, not all. Only mandatory default will catch all (unless the programmer is a f-wit, in which case ...)
  But
 I can see how porting legacy code directly from C would possibly allow
 different kinds of bugs to slip through.

Not if it was mandatory
 Why not have a compiler switch that can enforce the requirement for a
 default case - then everyone wins.

Walter won't have warnings, and wants to minimise the number of compilation modes. I agree with this. Given that stipulation, the clear position is to make the default (or unexpected) mandatory
Dec 03 2003
parent reply Georg Wrede <Georg_member pathlink.com> writes:
I've gone through reams of C code, and I am not convinced anymore
about the break statement. It really seems like the vast majority
of code does not need fallthrough, most of the switches are full
of breaks. Actually, cases without break (ie they fall through to
the next case) are much rarer than I originally thought. (Maybe
textbooks overrepresent examples of this?)

All fallthrough cases I found were empty themselves, so situations
where there's some code in one case after which the code of the
next is executed did not exist in my data. (Open Source from my 
Linux distro, a pseudorandom sample about 2.5M of c.)

This really blew my previous conviction. So, now I jump into the
"no break means break, fallthrough needs an explicit statement"
gang. 

If we think near the metal, then the old break thing seems natural,
but what we are aiming at is a higher level language here, so 
we gotta think outside the metal. Implicit break (heh, saves ink,)
gives clearer code, and I admit, now seems to be less dangerous
than demanding explicit break.

Since the cases where you need fallthroug are comparatively rare,
you sort of notice them, and there it is natural to have to write
a word more.

This change also suggests that we should allow lists in cases. This 
would also make it even rarer to need fallthrough.
Actually, we might consider skipping fallthrough totally.
The situations where you'd use it could be refactored, and we could
leave the optimizing to the optimizer. (Or if you insist on living
dangerously you could use (ohmigawd!) goto.)

This warns me of another thing: earlier I pointed out that not
being able to touch type should not influence our decisions about
the language. Similarly, ingrained coding habits, our pet idioms,
and "thinking in Pascal when writing Java"  -- shouldn't either
influence our current thinking about D!
Dec 03 2003
parent reply "Carlos Santander B." <carlos8294 msn.com> writes:
[warning: some of you might not find this idea appealing.]

It was suggested before, and now I'd like to bring it up again. I don't
think Walter will change the way switch works. Maybe he'll change the
default: status, but fallthrough will remain, and that's because he wants
some kind of C compatibility. (OT: btw, I don't agree with this. It's been
said many times, and I repeat: people shouldn't just copy and paste C code
for their D programs).

So maybe the next logical thing would be to add a different kind of switch
with all the things we all (by majority) have asked for: fallthrough,
explicit default, ranges, multiple case values, etc. What the keyword might
be, I don't know. Maybe Basic's select would fit.

—————————————————————————
Carlos Santander


---

Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.545 / Virus Database: 339 - Release Date: 2003-11-27
Dec 03 2003
next sibling parent reply J C Calvarese <jcc7 cox.net> writes:
Carlos Santander B. wrote:
 [warning: some of you might not find this idea appealing.]
 
 It was suggested before, and now I'd like to bring it up again. I don't
 think Walter will change the way switch works. 

 Maybe he'll change the
 default: status, but fallthrough will remain, and that's because he wants
 some kind of C compatibility. (OT: btw, I don't agree with this. It's been
 said many times, and I repeat: people shouldn't just copy and paste C code
 for their D programs).
 
 So maybe the next logical thing would be to add a different kind of switch
 with all the things we all (by majority) have asked for: fallthrough,
 explicit default, ranges, multiple case values, etc. What the keyword might
 be, I don't know. Maybe Basic's select would fit.

I think you might be on to something here. I think "select" would be a good keyword for this appealing new way to control the flow. This is how I'm forced to write it now: switch(varName) { case "icon": iconFilename = varValue; break; case "tooltip": toolTip = varValue; break; default: break; /* To prevent a runtime error if an invalid configuration setting is indicated. */ } Windows doesn't usually generate a GPF if you put something it doesn't understand in the system registry. I don't want my program to generate a runtime error if the user puts something untelligible in the configuration file. My program will just skip that line. This is how I'd like to write it: select(varName) { case "icon": iconFilename = varValue; case "tooltip": toolTip = varValue; /* If I wanted to include a default, I would have done so. */ } If the programmer wants an error to occur when a different value appears, they can specify it explicitly, case default: assert(0); <rant> Good programming practices can be encouraged by the language. Good programming practices can't be compelled. As the rules of a language approach compelling good practices, it would become unusable before it reached that apex. If I'm wrong on this, please tell me which language compels good practices, and tell me how much you like developing in that language. From my perspective, the only safe language for me is the one that I don't use. </rant> I'm not trying to be obnoxious; this is just the way I look at it. Justin
 
 —————————————————————————
 Carlos Santander

Dec 03 2003
parent reply "Walter" <walter digitalmars.com> writes:
"J C Calvarese" <jcc7 cox.net> wrote in message
news:bqlash$d74$1 digitaldaemon.com...
 <rant>
 Good programming practices can be encouraged by the language.  Good
 programming practices can't be compelled.  As the rules of a language
 approach compelling good practices, it would become unusable before it
 reached that apex.

 If I'm wrong on this, please tell me which language compels good
 practices, and tell me how much you like developing in that language.
  From my perspective, the only safe language for me is the one that I
 don't use.
 </rant>

 I'm not trying to be obnoxious; this is just the way I look at it.

I actually agree with you. D doesn't compel you to use good practices. Nowhere does D require you to use in and out contracts, class invariants, eshew pointers, etc. What it *does* do is provide facilities to make it easier to use the good practices than the bad ones. For example, using an out parameter is a better practice than passing an explicit pointer to the parameter. For another example, D lets you write quick and dirty programs that do no error checking on things like file open failures, disk write failures, etc. If such an error actually does happen, though, you get a nice message and the program terminates gracefully. This is quite unlike C, where if you forget to check for file open failures you're likely to get a seg fault, a hang, or other gloriously bad behavior. D is well suited to writing both quick-and-dirty programs as well as careful, thorough programs. I bet a lot of us write numerous one-shot dumb little utility quick-and-dirty programs (I know I do). There's a balance to be struck between a helpful compiler message and an annoying nag. Each of us obviously would want to draw that line in a slightly different place, but hopefully those lines aren't too far apart.
Dec 08 2003
parent reply The Lone Harnguer <The_member pathlink.com> writes:
Uh uh uh...

The compiler _does_ require good practices (in your opinion) or, in my opinion,
bad practices in some cases.

e.g.
if ( x = y )
which is flagged by the compiler and needs to have more code added to tell the
compiler that that is what I meant -- _that's bad_ !

And, even worse, it breaks ported C code as well.

In article <br3uce$1cvu$1 digitaldaemon.com>, Walter says...
I actually agree with you. D doesn't compel you to use good practices.
Nowhere does D require you to use in and out contracts, class invariants,
eshew pointers, etc. What it *does* do is provide facilities to make it
easier to use the good practices than the bad ones.

Dec 09 2003
parent J Anderson <REMOVEanderson badmama.com.au> writes:
The Lone Harnguer wrote:

Uh uh uh...

The compiler _does_ require good practices (in your opinion) or, in my opinion,
bad practices in some cases.

e.g.
if ( x = y )
which is flagged by the compiler and needs to have more code added to tell the
compiler that that is what I meant -- _that's bad_ !

And, even worse, it breaks ported C code as well.
  

need to break some C rules inorder to be better. IMHO An error message for if ( x = y ) is a good start, and it isn't to hard to fix. Hay, you can even find replace most of these with a good find-replace program. -Anderson
Dec 09 2003
prev sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Carlos Santander B. wrote:
 It was suggested before, and now I'd like to bring it up again. I don't
 think Walter will change the way switch works. Maybe he'll change the
 default: status, but fallthrough will remain, and that's because he wants
 some kind of C compatibility. (OT: btw, I don't agree with this. It's been
 said many times, and I repeat: people shouldn't just copy and paste C code
 for their D programs).
 
 So maybe the next logical thing would be to add a different kind of switch
 with all the things we all (by majority) have asked for: fallthrough,
 explicit default, ranges, multiple case values, etc. What the keyword might
 be, I don't know. Maybe Basic's select would fit.

I suggested something like that a while ago. I'll just paste part of that post again here: """ If we wouldn't have the requirement to easily port C/C++ code to D (do we really have it?) I would like to suggest to ditch the classic switch statement completely and introduce a new construct with different syntax that is more consistent with the rest of the language. This would also solve the problem that different fallthrough behaviour might confuse C/C++ programmers, since it is immediately recognizable as a different thing. Maybe something like this: branch(expression) { on(1) someFunc(); on(2,3,5..8) { a=7; dostuff(a,true); } } Hmmm. I think the syntax would probably need some work, but it would have some benefits: - the on-blocks are real code blocks. Adding local variables to them works in the same way as it does anywhere else. - break is not used in this construct, so it could actually be used to break out of an outer loop. This is something that I quite often wanted to do in C++. Ok, it is not THAT much of an issue in D anymore, since we can break with a label but being able to do it without an explicit label improves readability, since it is immediately clear which loop is referred to. But since I don't see any chance that switch is ditched there is no real point to argue for such a statement anyway . """ Hauke
Dec 03 2003
parent reply "Vathix" <vathix dprogramming.com> writes:
 Maybe something like this:

 branch(expression)
 {
      on(1)    someFunc();
      on(2,3,5..8)
      {
          a=7;
          dostuff(a,true);
      }
 }

 Hmmm. I think the syntax would probably need some work, but it would
 have some benefits:

 - the on-blocks are real code blocks. Adding local variables to them
 works in the same way as it does anywhere else.
 - break is not used in this construct, so it could actually be used to
 break out of an outer loop. This is something that I quite often wanted
 to do in C++. Ok, it is not THAT much of an issue in D anymore, since we
 can break with a label but being able to do it without an explicit label
 improves readability, since it is immediately clear which loop is
 referred to.

I like it. I think branch is a good name as well.
Dec 03 2003
parent reply Felix <Felix_member pathlink.com> writes:
Me too, I like it. Maybe D should implement both versions: switch and branch. As
do-while and while...


In article <bqmebi$21sh$1 digitaldaemon.com>, Vathix says...
 Maybe something like this:

 branch(expression)
 {
      on(1)    someFunc();
      on(2,3,5..8)
      {
          a=7;
          dostuff(a,true);
      }
 }

 Hmmm. I think the syntax would probably need some work, but it would
 have some benefits:

 - the on-blocks are real code blocks. Adding local variables to them
 works in the same way as it does anywhere else.
 - break is not used in this construct, so it could actually be used to
 break out of an outer loop. This is something that I quite often wanted
 to do in C++. Ok, it is not THAT much of an issue in D anymore, since we
 can break with a label but being able to do it without an explicit label
 improves readability, since it is immediately clear which loop is
 referred to.

I like it. I think branch is a good name as well.

Dec 04 2003
next sibling parent reply "Dan Liebgold" <dliebgold yahoo.com> writes:
Someone has probably already mentioned this, but C# <a
href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref
/html/vclrfTheSwitchStatement.asp">solved</a> the problem by keeping the
switch syntax, but disallowing fall-through. Instead you can do one of two
things: you can stack case statements, like so:

switch (index) {
  case 0:
  case 1:
    do_something();
    break;
}

And you can goto another case, like so:

switch (index) {
  case 0:
    do_something();
    goto case 1;
  case 1:
    do_something_more();
    break;
}

It doesn't address the question of throwing exceptions on unhandled cases,
but it neatly solves most of the issues brought up so far.  Can anyone give
an example of some code that relies on the fall-through behavior that this
won't solve?

Dan L.
Dec 05 2003
parent reply Richard Krehbiel <rich kastle.com> writes:
Dan Liebgold wrote:
 Someone has probably already mentioned this, but C# <a
 href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref
 /html/vclrfTheSwitchStatement.asp">solved</a> the problem by keeping the
 switch syntax, but disallowing fall-through. Instead you can do one of two
 things: you can stack case statements, like so:
 
 switch (index) {
   case 0:
   case 1:
     do_something();
     break;
 }
 
 And you can goto another case, like so:
 
 switch (index) {
   case 0:
     do_something();
     goto case 1;
   case 1:
     do_something_more();
     break;
 }
 
 It doesn't address the question of throwing exceptions on unhandled cases,
 but it neatly solves most of the issues brought up so far.  Can anyone give
 an example of some code that relies on the fall-through behavior that this
 won't solve?

Well, there's nothing that a goto can't "solve." :-) In fact, it's part of my own suggestion. ...which is: 1. case labels should allow enumerations: case 1, 3, 5: 2. case labels should allow ranges: case 6 .. 25: 3. the compiler should generate an implicit "break" at the end of each case - no fall-thrus, ever. 4. the programmer can use normal program labels and "goto" for every other need.
Dec 05 2003
parent Georg Wrede <Georg_member pathlink.com> writes:
The following is cut from D lexer.c:

case '/':
p++;
switch (*p)
{
case '=':
p++;
t->value = TOKdivass;
return;

case '*':
p++;
while (1)
{
while (1)
{
switch (*p)
{
case '/':
break;

case '\n':
loc.linnum++;
p++;
continue;

case 0:
case 0x1A:
error("unterm. /* */");
p = end;
t->value = TOKeof;
return;

default:
p++;
continue;
}
break;
}
p++;
if (p[-2] == '*' && p - 3 != t->ptr)
break;
}
continue;

case '/':
p++;
..

It looks as if you really can't code a lexer if there were
implicit breaks. But I think this would work the same
with them?

Of course an empty case should not get an implicit break.

I'm too old to be sure, but is there any place in phobos
where an implicit break would break the code? (No pun
intended.)
Dec 05 2003
prev sibling next sibling parent reply Georg Wrede <Georg_member pathlink.com> writes:
The D if-statement demands a {} instead of a semicolon.
The switch statement could do the same with the default
cause. Leave it out, and you get a compiler error.

If you want to not do anything, then just write
default
{}
and everyone should be happy. 

"It should not be impossible to excercise bad habits,
it just shouldn't be too easy."

PS, why doesn't the if statement issue a runtime
exception at a missing {} ? Shouldn't the compiler
behave the same in similar situations?   :-)
Dec 05 2003
parent "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
I like the cut of your jib

"Georg Wrede" <Georg_member pathlink.com> wrote in message
news:bqqpek$2cbs$1 digitaldaemon.com...
 The D if-statement demands a {} instead of a semicolon.
 The switch statement could do the same with the default
 cause. Leave it out, and you get a compiler error.

 If you want to not do anything, then just write
 default
 {}
 and everyone should be happy.

 "It should not be impossible to excercise bad habits,
 it just shouldn't be too easy."

 PS, why doesn't the if statement issue a runtime
 exception at a missing {} ? Shouldn't the compiler
 behave the same in similar situations?   :-)

Dec 05 2003
prev sibling parent reply Georg Wrede <Georg_member pathlink.com> writes:
The following should protect (at least newbies), and my
future D students:

...
case 0:
..
break;case 1:
..
break;case 2:
..
break;
case 50:
case 51:
case 52:
..
break;case 79:
..
break;case 80:
..
Dec 05 2003
parent reply Natsayer <Natsayer_member pathlink.com> writes:
Yuck, I hope I don't run up against any of your students, that's just uuuugly!

In article <bqqt9t$2i0c$1 digitaldaemon.com>, Georg Wrede says...
The following should protect (at least newbies), and my
future D students:

...
case 0:
..
break;case 1:
..
break;case 2:
..
break;
case 50:
case 51:
case 52:
..
break;case 79:
..
break;case 80:
..

Dec 05 2003
parent Georg Wrede <Georg_member pathlink.com> writes:
In article <bqr86c$b3$1 digitaldaemon.com>, Natsayer says...
Yuck, I hope I don't run up against any of your students, 
that's just uuuugly!

Yeah. That was the whole point. OTOH, during the first semester, these first year students have so much else to grasp, that finding a missing break in their own code is just unnecessary grief. The second semester, when they've gained some confidence in their coding we'll revisit the case statement and introduce fall-through. (IF, by that time, I have become convinced that there exist real-world reasons to it, outside nifty textbook examples!) I trust that the more talented students will find out how and when to use fall-through by themselves, but those who haven't might just be better off not using it. I'm also not going to teach about goto, by the same reason. In the limited time of a class, ther are more important things to teach. After all, they're there to learn how to program, and no class should teach every detail there is to a language just because they exist. I honestly believe D is an excellent first language. Maybe even better than Pascal (which actually was created for this very purpose)! D is also powerful enough to remain the main language through university. The other "obligatory languages" (Java C, C++, Lisp, whatever the particular university considers essential) should remain class-specific.
Dec 06 2003
prev sibling next sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
 It's very, very important that programs fail in a controlled manner when
 they encounter unanticipated conditions.

I might accept your solution if you make a minor change. Don't use assert(0);! As a programmer I might sprinkle asserts in my code and when I do, I know that some of my code (or at least the user of my code) has made a mistake and I might start to look for the assert in question. But suddenly I get an assert exception that don't match an assert in my code! Can be nothing else than an compiler bug. And I suppose I will flag it as such if it ever happens to me. The exception thrown MUST be a well documented and well specified exception (like the UnknownSwitchCaseException suggested by Matthew). Even better, demand the default case at compile time. Then it will never be a problem at runtime!

Exactly. What could be simpler?
Dec 03 2003
parent Antti =?iso-8859-1?Q?Syk=E4ri?= <jsykari gamma.hut.fi> writes:
In article <bqkb30$21du$1 digitaldaemon.com>, Matthew Wilson wrote:
 It's very, very important that programs fail in a controlled manner when
 they encounter unanticipated conditions.

[a lot of debate] Even better, demand the default case at compile time. Then it will never be a problem at runtime!

Exactly. What could be simpler?

There's been a lot of debate on the matter and a lot of different strong opinions, but I don't think we've still gone to the root of the problem. Namely: Which one is the primary usage of switch statement? One where you cover all the cases, or the one where you cover them only partially? Let's consider the alternatives. 1. If the programmer's intent is to cover all of the cases, then by default it should throw an exception if an unexpected value is encountered. Examples of this sort follow below. 2. If the intent is to cover only some of the cases, then the default behavior should do nothing. As an example of this: void handle_keypress(int code) { switch (code) { case 'q': quit(); break; case 'w': do_something_else; break; case ...: ...; // would not be needed if these kind of switches is common default: break; } } Here a default statement is required, and if it is not there, the programmer will notice very quickly when an exception flies. However, if it wouldn't be required (as in C/C++), not much harm would be done. Let's go back to the case 1: when we actually want to handle all cases. A classic representative of "handle all cases" comes when doing a switch over an enum: enum Weekday { mon, tue, wed, thu, fri, sat, sun } void handle(Weekday w) { switch (w) { case mon: handle_mon(); break; case tue: handle_tue(); break; // and so on... } } Now if you want to add a new weekday (ok, I might've picked my example a bit short-sightedly...), and forgot to add it to the switch statement, you should probably get an exception (or mayhap a warning, or even an error?) for not adding it to the switch statement. But this isn't often the way anyone should be programming at all. (Someone may disagree, though...) It's reasonably common to make Weekday a polymorphic object in this case: interface Weekday { void handle(); } class Monday : Weekday { void handle() { ... } } void handle(Weekday w) { w.handle(); } Then the 'missing case' of the switch statement won't be even left for the language runtime to notice, because it will be detected during the compilation! And everyone is happy. However, what if the value for the switch statement where we want to handle all possible cases doesn't come from the program itself but from outside, suppose while, say, lexing or parsing a source file in a compiler front-end, or maybe reading a file in some graphics format: void read_file() { File_Header hdr = get_header(); switch (hdr.bit_depth) { case 24: set_depth(24); break; case 16: ... break; case 15: ... break; case 8: ... break; // No default would have to be generated because we always want to // specify one: default: throw Invalid_File_Format_Exception( "Unknown bit depth " + hdr.bit_depth.to_string()); } } Ha! Here the automatically generated exception wouldn't help us anyway, because in this case we want to handle the default statement ourselves! So the only place where the automatically thrown exception does give any benefit is when there is an enum for which there is an excuse of not making it an object. Right? Then is there much sense in having the throw-exception behavior the default? Maybe someone knows good excuses? And I can't really say which of these cases (handle-all/handle-some) is the more common one. Maybe two different keywords were better from the documentative viewpoint. What do you people generally use "switch" for? -Antti P.S. By the way, to my ear the pascalese "case x of" sounds much like the case "now we're handling *all* of the possible alternatives". And the C-style "switch" really only deals with only some of the possible cases and is similar in semantics to if-elseif-elseif... withouth the final else.
Dec 03 2003
prev sibling next sibling parent reply Berin Loritsch <bloritsch d-haven.org> writes:
In Java we are used to the language runtime issuing exceptions for
things that are out of the ordinary, such as: NullPointerException,
ClassCastException, IndexOutOfBoundsException, etc.

The use of the "assert(0)" as the default "default" clause is not
really my beef.  What I really want is to know exactly where the
error happened.  I can't know that if all I get is "Assertion Error"
from the application.  I need a stack trace to find out where this
assertion happened.  What would be even better is to use a better
typed exception like this:

default: throw new UnexpectedCaseException(caseValue);

That way we can output the caseValue, an exception that makes sense.
But even more important some very basic debug info like where the
stinkin' exception was thrown is imperitive.

Imagine having an application that has 100 switch statements, and
they encounter this unexpected case with the default: assert(0) semantics.
Debugging is a nightmare!  Where to look?  Which one of these 100 switch
statements is the culprit?  If it was the culprit, what was the value
that caused the exception?

These are very important pieces of information that I think would be
more reasonable than a simple assert(0).
Dec 03 2003
parent "Walter" <walter digitalmars.com> writes:
"Berin Loritsch" <bloritsch d-haven.org> wrote in message
news:bqks57$2pa4$1 digitaldaemon.com...
 The use of the "assert(0)" as the default "default" clause is not
 really my beef.  What I really want is to know exactly where the
 error happened.  I can't know that if all I get is "Assertion Error"
 from the application.  I need a stack trace to find out where this
 assertion happened.  What would be even better is to use a better
 typed exception like this:

 default: throw new UnexpectedCaseException(caseValue);

 That way we can output the caseValue, an exception that makes sense.
 But even more important some very basic debug info like where the
 stinkin' exception was thrown is imperitive.

I agree. This is what D already does: C:\>type test.d void main() { switch (3) { case 1: break; } } C:\>dmd test Max # of fixups = 4 \dm\bin\link test,,,user32+kernel32/noi; C:\>test Error: Switch Default test.d(4) C:\>
Dec 08 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Lars Ivar Igesund" <larsivar igesund.net> wrote in message
news:bqk56n$1o0g$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:bqjanc$gfp$1 digitaldaemon.com...
 It's very, very important that programs fail in a controlled manner when
 they encounter unanticipated conditions.

I might accept your solution if you make a minor change. Don't use assert(0);!

I apologize for speaking metaphorically, what actually happens is not an assert(0) but a SwitchError exception is thrown. It'll give the file and line number, too, so it shouldn't be hard to find where it's coming from.
 The exception thrown MUST be a well documented and well
 specified exception (like the UnknownSwitchCaseException suggested
 by Matthew).

I agree. And the documentation says: "If none of the case expressions match, and there is not a default statement, a SwitchException is thrown." www.digitalmars.com/d/statement.html#switch. <g> (Actually, the documentation is wrong, it's now a SwitchError.)
 Even better, demand the default case at compile time. Then it will
 never be a problem at runtime!

I'm reluctant to do that from experience with Java's demands that all exceptions be handled. The result was people would just blindly insert catch(...) to shut up the compiler. The result was *worse* because now new exceptions that *needed* to be dealt with got silently swallowed. (I've had some long discussions with well-known Java gurus about this.) It was one of those things that looked great on paper, but failed in practice.
Dec 08 2003
parent reply Felix <Felix_member pathlink.com> writes:
I agree, esp. with the last point. Things are a lot clearer for me now. So, no
"default" case required.


In article <br3t6m$1bd1$1 digitaldaemon.com>, Walter says...
"Lars Ivar Igesund" <larsivar igesund.net> wrote in message
news:bqk56n$1o0g$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:bqjanc$gfp$1 digitaldaemon.com...
 It's very, very important that programs fail in a controlled manner when
 they encounter unanticipated conditions.

I might accept your solution if you make a minor change. Don't use assert(0);!

I apologize for speaking metaphorically, what actually happens is not an assert(0) but a SwitchError exception is thrown. It'll give the file and line number, too, so it shouldn't be hard to find where it's coming from.
 The exception thrown MUST be a well documented and well
 specified exception (like the UnknownSwitchCaseException suggested
 by Matthew).

I agree. And the documentation says: "If none of the case expressions match, and there is not a default statement, a SwitchException is thrown." www.digitalmars.com/d/statement.html#switch. <g> (Actually, the documentation is wrong, it's now a SwitchError.)
 Even better, demand the default case at compile time. Then it will
 never be a problem at runtime!

I'm reluctant to do that from experience with Java's demands that all exceptions be handled. The result was people would just blindly insert catch(...) to shut up the compiler. The result was *worse* because now new exceptions that *needed* to be dealt with got silently swallowed. (I've had some long discussions with well-known Java gurus about this.) It was one of those things that looked great on paper, but failed in practice.

Dec 09 2003
parent reply Berin Loritsch <bloritsch d-haven.org> writes:
Felix wrote:

I'm reluctant to do that from experience with Java's demands that all
exceptions be handled. The result was people would just blindly insert
catch(...) to shut up the compiler. The result was *worse* because now new
exceptions that *needed* to be dealt with got silently swallowed. (I've had
some long discussions with well-known Java gurus about this.) It was one of
those things that looked great on paper, but failed in practice.


Yes and no. Being a Java guru, most of the issues resolve around people who do not know how to work with exceptions. Think about it. How many C++ guys understand exceptions and how to properly handle them, and how many Java guys understand exceptions and how to properly handle them? The thing is that the closer to the area of the thrown exception you are, the better chance you have of working around it. I actually think that RuntimeExceptions (AKA unchecked exceptions) can be more damaging to an application's stability than checked ones. Will there be newbies that don't know that catch(Throwable) is bad--I mean really bad? Of course there will be. There will always be new developers who have to learn the hard way. There will always be lazy developers who don't *want* to learn the proper way. These developers cause more issues for contienscious developers than anything else. Think about it, for some things it is obvious that an exception will be thrown. If you are opening a file to read, there is a chance that the file won't exist. If it doesn't, and you still try to open it, you *should* expect a FileNotFoundException, and you should handle it as close to the source of the problem as possible. If that file was supposed to be a configuration file, then we might have to abort the program with another exception. The exception is caused by the FileNotFoundException, but the reason the app aborted is a ConfigNotFoundException or something. We can get into all kinds of philisophical discussions, but broad sweeping statements are generally made by people who want to shade the facts toward their personal point of view. Could you use a knife to loosen a screw? Sure, but I wouldn't advise it. The point is that there are different tools for different jobs. Checked exceptions have their uses--and many times they are an elegant solution. Unchecked exceptions also have their uses. I have seen examples where either type of exception was the wrong tool for the job. Saying that it is "a failed experiment" is FUD. It fails to understand the problem or the proposed solutions. What you decide for this language is up to you--just don't base decisions on broad sweeping statements is all.
Dec 09 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Berin Loritsch" <bloritsch d-haven.org> wrote in message
news:br4q6i$2s7e$1 digitaldaemon.com...
I'm reluctant to do that from experience with Java's demands that all
exceptions be handled. The result was people would just blindly insert
catch(...) to shut up the compiler. The result was *worse* because now



exceptions that *needed* to be dealt with got silently swallowed. (I've



some long discussions with well-known Java gurus about this.) It was one



those things that looked great on paper, but failed in practice.


Yes and no. Being a Java guru, most of the issues resolve around people

 do not know how to work with exceptions.  Think about it.  How many C++

 understand exceptions and how to properly handle them, and how many Java

 understand exceptions and how to properly handle them?

The Java gurus would tell me that *they themselves* would insert the catch(...)'s, even while publicly preaching against it. They knew all the arguments against doing such, as they came up with those arguments! It wasn't just an issue of ignorant/inexperienced Java coders. C++ has this problem with "const-correctness". People find themselves stuffing in const keywords and const casts willy-nilly to get the code to compile. (There are even jokes on the internet about this practice.) While the end result will then compile, it isn't exactly "const-correct", little was achieved by it, and I would hazard a guess that few non-trivial C++ programs are actually const-correct because of this.
 We can get into all kinds of philisophical discussions, but broad sweeping
 statements are generally made by people who want to shade the facts toward

 personal point of view.  Could you use a knife to loosen a screw?  Sure,

 I wouldn't advise it.  The point is that there are different tools for

 jobs.  Checked exceptions have their uses--and many times they are an

 solution.  Unchecked exceptions also have their uses.

 I have seen examples where either type of exception was the wrong tool for

 job.  Saying that it is "a failed experiment" is FUD.  It fails to

 the problem or the proposed solutions.  What you decide for this language

 up to you--just don't base decisions on broad sweeping statements is all.

While I generally agree with you here, the point I make is that the language should make the right thing to do the easiest thing to do. In Java, the right thing to do with checked exceptions is the most tedious thing to do. The easiest thing to do in Java is just do a catch(...) and forget about it. This is much, much worse than not catching an unexpected unchecked exception (and so letting the runtime eventually handle it). In fact, if I have my Java history right, the whole reason that the Java language eventually added unchecked exceptions at all was the discovery that requiring all exceptions to be checked was unworkable for just the reasons I described.
Dec 09 2003
next sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
I'm reluctant to do that from experience with Java's demands that all
exceptions be handled. The result was people would just blindly insert
catch(...) to shut up the compiler. The result was *worse* because now



exceptions that *needed* to be dealt with got silently swallowed.




 had
some long discussions with well-known Java gurus about this.) It was




 of
those things that looked great on paper, but failed in practice.


Yes and no. Being a Java guru, most of the issues resolve around people

 do not know how to work with exceptions.  Think about it.  How many C++

 understand exceptions and how to properly handle them, and how many Java

 understand exceptions and how to properly handle them?

The Java gurus would tell me that *they themselves* would insert the catch(...)'s, even while publicly preaching against it. They knew all the arguments against doing such, as they came up with those arguments! It wasn't just an issue of ignorant/inexperienced Java coders. C++ has this problem with "const-correctness". People find themselves stuffing in const keywords and const casts willy-nilly to get the code to compile. (There are even jokes on the internet about this practice.) While the end result will then compile, it isn't exactly "const-correct", little was achieved by it, and I would hazard a guess that few non-trivial C++ programs are actually const-correct because of this.

This is simply wrong. I hear about this from time to time, and it is invariably a lack of care or understanding on the part of the developer. I can't think of a single instance where I've had to violate const-correctness. The few times where I've used const-cast are where it's doing lazy eval, or updating member stats, both of which are perfectly valid. While your Java exceptions analogy may have some merit, using C++'s const-correctness does not.
Dec 09 2003
parent reply Berin Loritsch <bloritsch d-haven.org> writes:
Matthew Wilson wrote:

I'm reluctant to do that from experience with Java's demands that all
exceptions be handled. The result was people would just blindly insert
catch(...) to shut up the compiler. The result was *worse* because now



The Java gurus would tell me that *they themselves* would insert the catch(...)'s, even while publicly preaching against it. They knew all the arguments against doing such, as they came up with those arguments! It wasn't just an issue of ignorant/inexperienced Java coders.


Maybe it was and they just didn't want to admit it. ;P Seriously, it isn't so much whether you catch(Exception) (more acceptable than catch(Throwable)) or not is not the major issue, it is what you do with it from there. If you rethrow things you don't intend to worry about right away, then all is good. For every one you can claim is doing it wrong even though they know better, I can point to several more that do it right--without complaint.
C++ has this problem with "const-correctness". People find themselves
stuffing in const keywords and const casts willy-nilly to get the code to
compile. (There are even jokes on the internet about this practice.) While
the end result will then compile, it isn't exactly "const-correct", little
was achieved by it, and I would hazard a guess that few non-trivial C++
programs are actually const-correct because of this.

This is simply wrong. I hear about this from time to time, and it is invariably a lack of care or understanding on the part of the developer. I can't think of a single instance where I've had to violate const-correctness. The few times where I've used const-cast are where it's doing lazy eval, or updating member stats, both of which are perfectly valid. While your Java exceptions analogy may have some merit, using C++'s const-correctness does not.

Again, this is why I call it FUD. You simply do not know. For everyone that is doing it right there are others that do it wrong. So what to do? IMO, make a better mechanism to handle these things. If you can't learn from what is out there. I would prefer to know about potential exceptions if I call a method--esp. if I can do something about it. For instance: "Ok this file doesn't exist, let's use this one as a backup". Or no config could be loaded so we are working with defaults. Whatever. If a method will throw a known exception I want something stronger than a comment to tell me about it. Things that are inadvertant errors such as illegal argument exceptions, bounds checking exceptions or other obvious runtime exceptions, then I don't want to know about them or worry about them. I think having a mix of both checked and unchecked exceptions provides a nice way to ensure things are working properly--within reason. So if you have some system generated exceptions for violations of code constraints, of course they should be unchecked. It is quite common for people to filter whatever they hear to fit their own little world. For example, if you don't want checked exceptions, ok. But give solid reasoning beyond so and so said... Have you worked with a well written system? Have you worked with a poorly written system? What was the *real* problem? 9 times out of 10 is was using the wrong tool for the job--not that the tool didn't exist. I brought up C++ exception handling (your catch(...) examples you keep listing) because I have run into fewer C++ developers who know how to properly work with exceptions than Java developers. There are a couple reasons for this observation. First is that many C++ developers use C libraries which force you to do a check on a status bit returned from a method. This results in code with a plethora of if(!methodCall()) {} error handling statements. The second main reason is that until the last couple of years there haven't been compilers that produced decent exception handling code. Lastly, I will say that I much prefer Java's version of the catchall statement than C++'s. Here is the main difference: C++: catch(...) { // how do I get the exception, and report info on it?!? } Java: catch(Exception e) { e.printStackTrace(); System.out.println( e.getClass().getName() ); // find the root cause Throwable cause = e; while (e.getCause() != null) { cause = e.getCause(); } } There are a number of things you can do to have a meaningful error reported and present the user with a nice message to make it easier to understand what to do. Should catch alls be avoided? sure, but I have seen them more often in C++ code I have had the pleasure of maintaining than Java.... The bottom line is EVERY language has its weaknesses. EVERY language has its strengths. Somethimes the same thing can be a weekness and a strength. It depends on how it is used. Making blanket statements is like throwing the baby out with the bath water.
Dec 09 2003
parent Hauke Duden <H.NS.Duden gmx.net> writes:
Berin Loritsch wrote:
 If a method will throw a known exception I want something stronger than a
 comment to tell me about it.
 
 Things that are inadvertant errors such as illegal argument exceptions,
 bounds checking exceptions or other obvious runtime exceptions, then I 
 don't
 want to know about them or worry about them.  I think having a mix of both
 checked and unchecked exceptions provides a nice way to ensure things are
 working properly--within reason.

That won't suffice. The main problem I have with JAVA's forced throws clause is that it prevents proper use of interfaces. (There are other flaws, like the insane amount of throws clauses you get if the call hierarchy gets deep enough, but the interface problem is the most important one to me.) The main point in having interfaces is to abstract from the implementation. But if you have to define what kinds of errors can occur in the implementation, then you automatically limit the "back-ends" you can put behind it. A great example is if you want to implement an existing interface to forward calls over a network to a server and let the server perform the task. Suddenly there are a whole lot of additional errors, like "host not found", "connection lost" and stuff like that that were not anticipated by whoever wrote the original interface. So what you end up with is either ignoring such errors or wrapping them in an object of one of the "legal" classes. Both ways are bad. The first one hides errors and can have serious consequences for the ability of the program to work properly. The second one defeats the whole purpose of having different exception classes, as they often end up being wrapped in some unrelated exception type. After having been frustrated with this "feature" of JAVA multiple times I just decided to ignore it for my last project. In that program EVERY method has a "throws Exception" clause. I was just fed up with trying to keep up meaningful error handling in an interface-based program while the compiler was working against me. Hauke
Dec 09 2003
prev sibling next sibling parent "Walter" <walter digitalmars.com> writes:
Here's an article on the topic:

http://www.octopull.demon.co.uk/java/ExceptionalJava.html

Also, I checked Java 1.0. There was a facility for unchecked exceptions in
it, so I made a mistake saying it wasn't there. I apologize for the error.

Here's another article:

http://www.mindview.net/Etc/Discussions/CheckedExceptions
Dec 09 2003
prev sibling next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Remind me, Walter.  Why don't we have (optional) throws statements?

It seems to me like they would be the best of both worlds - they would 
allow additional DBC (since 'throws' is essentially an out clause) 
without requiring people to use kludgy catch(...).

I can imagine that this might be difficult to enforce since the compiler 
would have to analyze all called functions (unless, of course, they also 
have 'throws' clauses).  OTOH, you could throw an assertion failure, 
just like you would when you failed any other out statement.  (Russ 
ducks flaming arrows headed his way...)

Russ

Walter wrote:
 "Berin Loritsch" <bloritsch d-haven.org> wrote in message
 news:br4q6i$2s7e$1 digitaldaemon.com...
 
I'm reluctant to do that from experience with Java's demands that all
exceptions be handled. The result was people would just blindly insert
catch(...) to shut up the compiler. The result was *worse* because now



new
exceptions that *needed* to be dealt with got silently swallowed. (I've



had
some long discussions with well-known Java gurus about this.) It was one



of
those things that looked great on paper, but failed in practice.


Yes and no. Being a Java guru, most of the issues resolve around people

who
do not know how to work with exceptions.  Think about it.  How many C++

guys
understand exceptions and how to properly handle them, and how many Java

guys
understand exceptions and how to properly handle them?

The Java gurus would tell me that *they themselves* would insert the catch(...)'s, even while publicly preaching against it. They knew all the arguments against doing such, as they came up with those arguments! It wasn't just an issue of ignorant/inexperienced Java coders. C++ has this problem with "const-correctness". People find themselves stuffing in const keywords and const casts willy-nilly to get the code to compile. (There are even jokes on the internet about this practice.) While the end result will then compile, it isn't exactly "const-correct", little was achieved by it, and I would hazard a guess that few non-trivial C++ programs are actually const-correct because of this.
We can get into all kinds of philisophical discussions, but broad sweeping
statements are generally made by people who want to shade the facts toward

their
personal point of view.  Could you use a knife to loosen a screw?  Sure,

but
I wouldn't advise it.  The point is that there are different tools for

different
jobs.  Checked exceptions have their uses--and many times they are an

elegant
solution.  Unchecked exceptions also have their uses.

I have seen examples where either type of exception was the wrong tool for

the
job.  Saying that it is "a failed experiment" is FUD.  It fails to

understand
the problem or the proposed solutions.  What you decide for this language

is
up to you--just don't base decisions on broad sweeping statements is all.

While I generally agree with you here, the point I make is that the language should make the right thing to do the easiest thing to do. In Java, the right thing to do with checked exceptions is the most tedious thing to do. The easiest thing to do in Java is just do a catch(...) and forget about it. This is much, much worse than not catching an unexpected unchecked exception (and so letting the runtime eventually handle it). In fact, if I have my Java history right, the whole reason that the Java language eventually added unchecked exceptions at all was the discovery that requiring all exceptions to be checked was unworkable for just the reasons I described.

Dec 09 2003
prev sibling parent Ilya Minkov <minkov cs.tum.edu> writes:
Walter wrote:

 While I generally agree with you here, the point I make is that the language
 should make the right thing to do the easiest thing to do. In Java, the
 right thing to do with checked exceptions is the most tedious thing to do.
 The easiest thing to do in Java is just do a catch(...) and forget about it.

You don't need the experts to tell you that. I just took 2 randomly bought Java books from my bookshelf, and discover that they both teach the same practice... One being "Java 2 Wochenend Crashkurs" (MITP) and another "Java 2 Kompendium" (Markt&Technik). I believe the second is quite popular out here... And if the novices are not taught to get it right from beginning on, they probably realize that it's wrong when it's much too late. I'm somewhat thankful to parts of the library which don't force declaring eceptions on me... BTW, i've been rethinking my strategy about warnings ("nags")... I think now i'm more or less with you. The major difference to other warnings is though, that it is requiered to add these "throws" specifications not on one functiones, but on all which possibly can call it, including indirect calls, and that's what makes throw specification so evil. The number of changes requiered explodes with the growing project size. Change the underlying implementation of one function, so that the part of the library it uses may throw another exception, and there you have it - go ahead and correct the whole call tree. My current solution would be, to catch these exceptions with an assert if i don't expect them anyway... -eye --- rant --- 8< --- come to think of it, i don't know of a single Delphi book to teach questionable practices. however, C++ and java books teaching bad practice are quite common... while with c++ it is somehow explained by complexity - which is imo just a bad excuse for being unable to write a good book, or to get hands on writing a book for *novices* without understanding the language - for java it's really incredible... everyone knows, we should not do this and that, but we do and teach it anyway... that's where i can say the language is flawed... maybe another reason for good Delphi books is that they had to rival with the comprehensive, thought-through, wise manual (actually a tutorial book) delivered with each Delphi package? all delphi books seem to share a ton of similarity with that original work... something we should think about when it comes to our manuals someday. --- >8 ---
Dec 09 2003
prev sibling next sibling parent reply Ant <Ant_member pathlink.com> writes:
In article <bqivh7$cr$1 digitaldaemon.com>, Walter says...
"Ilya Minkov" <minkov cs.tum.edu> wrote in message
news:bqaci5$2rl3$2 digitaldaemon.com...
 Requiring default will bring us to people typing "default: break;"
 instead of "default: assert(0)"  just exactly for the reason you stated!

[...]

Philosophically, I don't think a language is robust because it requires
programmers to code in a certain way.

You are contradicting your self because you are forcing programers to have a "defaul:assert(0);". even if they don't know about it!
[...]

Ant
Dec 02 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Ant" <Ant_member pathlink.com> wrote in message
news:bqj3b0$5vc$1 digitaldaemon.com...
 You are contradicting your self because you are forcing programers
 to have a "defaul:assert(0);". even if they don't know about it!

But D doesn't force them to explicitly write it! Sorry if I wasn't clear about that.
Dec 02 2003
next sibling parent reply Ant <Ant_member pathlink.com> writes:
In article <bqj778$bkj$1 digitaldaemon.com>, Walter says...
"Ant" <Ant_member pathlink.com> wrote in message
news:bqj3b0$5vc$1 digitaldaemon.com...
 You are contradicting your self because you are forcing programers
 to have a "defaul:assert(0);". even if they don't know about it!

But D doesn't force them to explicitly write it! Sorry if I wasn't clear about that.

You can't convince me on this one ;) I have nothing to add to mine or Mathew's first posts on this thread. Also I never saw you change your mind so I'm loosing hope on this one. (reverse psichology. again! but this time no spell check. didn't work the first time :) Ant
Dec 02 2003
parent "Walter" <walter digitalmars.com> writes:
"Ant" <Ant_member pathlink.com> wrote in message
news:bqj888$d1r$1 digitaldaemon.com...
 You can't convince me on this one ;)

I'll wait for when it finds an overlooked bug in your code <g>.
Dec 02 2003
prev sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bqj778$bkj$1 digitaldaemon.com...
 "Ant" <Ant_member pathlink.com> wrote in message
 news:bqj3b0$5vc$1 digitaldaemon.com...
 You are contradicting your self because you are forcing programers
 to have a "defaul:assert(0);". even if they don't know about it!

But D doesn't force them to explicitly write it! Sorry if I wasn't clear about that.

So what? It does force them to have it. This is so bogus. You are forcing something on people that probably a lot of them do not want. How is that in keeping with the philosophy of not forcing things on people? Which is more odious, a forced compile-time restriction, or a forced runtime restriction?
Dec 02 2003
parent reply "Walter" <walter digitalmars.com> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bqjqtm$18hg$2 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:bqj778$bkj$1 digitaldaemon.com...
 "Ant" <Ant_member pathlink.com> wrote in message
 news:bqj3b0$5vc$1 digitaldaemon.com...
 You are contradicting your self because you are forcing programers
 to have a "defaul:assert(0);". even if they don't know about it!

about that.

This is so bogus. You are forcing something on people that probably a lot

 them do not want. How is that in keeping with the philosophy of not

 things on people?
 Which is more odious, a forced compile-time restriction, or a forced

 restriction?

I think both are odious for the switch, so as with all the runtime checks, they are not generated when compiled with -release. Though if one doesn't have a good test suite, it might be a good idea to not use -release and leave the runtime checks in.
Dec 08 2003
parent J C Calvarese <jcc7 cox.net> writes:
Walter wrote:
 "Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
 news:bqjqtm$18hg$2 digitaldaemon.com...
 
"Walter" <walter digitalmars.com> wrote in message
news:bqj778$bkj$1 digitaldaemon.com...

"Ant" <Ant_member pathlink.com> wrote in message
news:bqj3b0$5vc$1 digitaldaemon.com...

You are contradicting your self because you are forcing programers
to have a "defaul:assert(0);". even if they don't know about it!

But D doesn't force them to explicitly write it! Sorry if I wasn't clear about that.

So what? It does force them to have it. This is so bogus. You are forcing something on people that probably a lot

of
them do not want. How is that in keeping with the philosophy of not

forcing
things on people?
Which is more odious, a forced compile-time restriction, or a forced

runtime
restriction?

I think both are odious for the switch, so as with all the runtime checks, they are not generated when compiled with -release. Though if one doesn't have a good test suite, it might be a good idea to not use -release and leave the runtime checks in.

OK, I disagree with the current switch statement mostly in ways that are diametrically opposed to the opinion of Matthew Wilson, but I'm going to support his arguments since I see it as the lesser of "two evils" (so to speak): *Let's require a case on each switch statement.* This is better than the current situation of "random runtime errors" that I have. I'll come out and say it: I usually don't cover all of the case. I don't want to cover them all. (Oh, no! I must be a bad programmer.) If I'm reading in a simple little configuration file for a simple little application, I don't want a runtime error because the user puts an unknown setting in a line. I'll just ignore it. So I put "default: break;" at the end of the switch. So I'd rather D complain about it at compile time to remind me to add my "default: break;" and you and Matthew will remember to add your "default: assert(0);" If the errors are comping to occur due to my style of programming, I'd prefer them to appear at compile time when they're less embarrassing. (Of course, I'd argue going the opposite direction tommorrow -- hands-off compiling/no runtime checking -- if you show any signs of softening up on that idea.) Justin
Dec 09 2003
prev sibling parent "Sean L. Palmer" <palmer.sean verizon.net> writes:
I think that you should not just code "any which way you want to" because
your program will be full of bugs and will crash.  There are plenty of
programming "styles" that are just plain bad bad bad and should be
prohibited.

The compiler should try to help you get rid of bugs by detecting them at
compile time, *before* you ship your application to millions of people.  I
realize this is not 100% possible.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:bqivh7$cr$1 digitaldaemon.com...
 "Ilya Minkov" <minkov cs.tum.edu> wrote in message
 news:bqaci5$2rl3$2 digitaldaemon.com...
 Requiring default will bring us to people typing "default: break;"
 instead of "default: assert(0)"  just exactly for the reason you stated!

A similar problem has happened with Java where it required you to list the possible exceptions generated by each function. Even expert programmers

 publicly excoriated the practice would pepper their own code with generic
 catches just to shut up the compiler. The end result was that a rule that
 was supposed to increase robustness, actually wound up making things

 Philosophically, I don't think a language is robust because it requires
 programmers to code in a certain way. I think it's robust if it allows
 programmers to program the way they want to, making robust programming
 practices easier to use than non-robust ones. For example, although D

 you to use pointers as you would in C, D provides better semantic
 alternatives (like out parameters) that are more robust, and easier to use
 than the pointer equivalents. It's a win-win.

 One can counter this by saying "why do static type checking at all, then?
 Why not do it at runtime?" It's an excellent point, and the answer is that
 it's just too expensive to do at runtime. Languages that rely on runtime
 type checking tend to run very slowly compared with statically typed
 languages. As always, everything is a compromise in programming, and so we
 have static type checking in D.

Dec 03 2003
prev sibling parent Berin Loritsch <bloritsch d-haven.org> writes:
Following is Walters reasoning why the default checking can't be done at
compile time.

The thing is that we should either make default required all the time--which
is an easy compile-time check--or follow normal switch semantics.  Otherwise
you will run into folks who only want to run some processing in one of a few
cases, but ignore all other cases, and wonder why there is some sort of runtime
exception being thrown.

If you still want something that is runtime checked for these types of things,
perhaps make it a feature of compiling with debug info in place that a message
would be output to the system error stream with enough debug info to know a)
what the value of the variable was, b) where the problem occurred.

That way, there is enough information not only to know something is up, but to
intelligently decide if it is really a problem or not.

Walter wrote:

 "Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
 news:bq65gu$2n8o$1 digitaldaemon.com...
 
Have to agree here, I rareley ( never ? ) use exceptions , and dont like
them being thrown without my knoweldge / consent.

Exactamundo, my friend. Exceptions are great for somethings, including actual serious/fatal

errors,
or when doing stuff like deep-level parsing.

Where they are absolutely not appropriate - and I can't believe this is

even
a discusson - is a runtime report of a code-time mistake!!

There's no way to detect the error at compile time. What it's for is when one has: #1 switch (x) { case 1: ... case 2: ... case 3: ... } and then one day x has the value 4. In C, there's an implicit default:break; inserted when no default is explicitly supplied. But in my experience coding, debugging my own mistakes, and fixing other peoples' code, the implicit default break is almost always the WRONG thing to happen, and then something unexpected happens as a result (i.e. crash, data corruption, etc.). When I see C code like that, and can ask the author, the intention is that x will always be 1, 2 or 3 and NEVER anything else. That's why he wrote the switch that way. That's ok, but then the maintenance programmer adds a feature where x is 4, updates all the switch statements accordingly, but inevitably misses one in the 100,000 line program he's revising. Even if the original coder INTENDED to make use of the implicit default:break;, it has that distinct odor of a bug, and so I flag it in code reviews. And in my experience, it was never intended. For years, I've advocated 'defensive programming' in C by making it explicit that the default case can never happen with: #2 switch (x) { case 1: ... case 2: ... case 3: ... default: assert(0); } This is the normal practice in my own code. There are 3 situations to deal with, the above one, and: #3 switch (x) { case 1: ... case 2: ... case 3: ... default: break; } and: #4 switch (x) { case 1: ... case 2: ... case 3: ... default: do something break; } What D does is make it easy to ensure that all the bases are covered by generating #2 if a default is not supplied. You're not going to get caught with a random crash from forgetting to deal with a case. The compiler cannot do this at compile time because it has no way of determining all possible values x can take. In that way, it's similar to run time array bounds checking. The only other way of doing this with a hope of robustness is to *require* an explicitly written default statement for every switch. This would certainly be a valid language strategy, but I don't really want to be nagged by the compiler to insert a default:assert(0); when I know darn well that x can never be 4 <g>.

Dec 01 2003
prev sibling parent Berin Loritsch <bloritsch d-haven.org> writes:
Matthew Wilson wrote:
Have to agree here, I rareley ( never ? ) use exceptions , and dont like
them being thrown without my knoweldge / consent.

Exactamundo, my friend. Exceptions are great for somethings, including actual serious/fatal errors, or when doing stuff like deep-level parsing. Where they are absolutely not appropriate - and I can't believe this is even a discusson - is a runtime report of a code-time mistake!!

Sounds like it is calling for a compiler check to me. I don't mind if the switch statement REQUIRES a default statement--as long as the compiler reports the problem.
Dec 01 2003
prev sibling next sibling parent reply "Vathix" <vathix dprogramming.com> writes:
How about a different word for case, like bcase, casex, fcase or ...
briefcase. I don't know.

switch(foobar)
{
case 3: normalFallThrough();
bcase 4: breaksAfterThis();
bcase 5, 6, 7: goodIdea();
default: yes();
}
Sep 16 2003
parent Richard Krehbiel <rich kastle.com> writes:
My Humble Opinion re: switch:

1. Individual cases should support enumerations and ranges.  Like this:

	case 1, 2, 3:  // switch values is 1, 2, or 3
	case 50 .. 100: // switch value between 50 and 100 inclusive

2. All cases should imply break.

3. All other needs are well satisfied with goto.  Yes, I said goto.

In my experience, 90+% of the need for fallthru is to support 
enumerations, so I'd definitely lobby for that support.  The case ranges 
allow switch to be used for more things that if/elseif/else has been 
used for, leading to the possibility of better diagnostics and greater 
language optimization.

And when all else fails, there's goto.  Yes, it's hated, which puts 
pressure on the programmer to think harder to make sure that it's really 
needed, and that's a GOOD thing because it's probably not.  But if they 
can justify it, then it's right there, available for use.
Sep 17 2003
prev sibling next sibling parent J Anderson <anderson badmama.com.au.REMOVE> writes:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1">
  <title></title>
</head>
<body text="#000000" bgcolor="#ffffff">
<div class="moz-text-html" lang="x-western"> Ian Woollard wrote:<br>
<blockquote type="cite" cite="midbk5pk9$1a54$1 digitaldaemon.com">
  <pre wrap="">In article <a class="moz-txt-link-rfc2396E"
 href="mailto:bk5o47$15o3$1 digitaldaemon.com">&lt;bk5o47$15o3$1 digita
daemon.com&gt;</a>, Charles Sanders says...
  </pre>
  <blockquote type="cite">
    <pre wrap="">I like the idea of a nobreak statement ( this has been a
popular topic) , I
think the reason for keeping this is backward compatibility ( on a user
level ).
    </pre>
  </blockquote>
  <pre wrap=""><!---->
Provided the nobreak statement is optional (just a nasty compiler warning if
left out), or mandatory but the compiler has a flag to make it ignore it's
omission (but still warn- my colleagues ARE out to get me :-) ), then backward
compatibility is, in practice, a non issue.

  </pre>
  <blockquote type="cite">
    <pre wrap="">Charles

"Ian Woollard" <a class="moz-txt-link-rfc2396E"
 href="mailto:Ian_member pathlink.com">&lt;Ian_member pathlink.com&gt;</a>
wrote in message
<a class="moz-txt-link-freetext"
 href="news:bk5nri$14cm$1 digitaldaemon.com">news:bk5nri$14cm$1 digitaldaemon.com</a>...
    </pre>
    <blockquote type="cite">
      <pre wrap="">Just had a quick look at the switch statement.

I noticed the syntax is:

switch (foobar)
{
case 0:
dostuff();
// I've forgotten to break; all is lost.
case 1:
domorestuffIdidntreallywantto();
break;
default:
}

This isn't such a good idea. The default behaviour is really unsafe. I
      </pre>
    </blockquote>
    <pre wrap="">would
    </pre>
    <blockquote type="cite">
      <pre wrap="">suggest a 'nobreak' statement for cases where you really do
want the code
      </pre>
    </blockquote>
    <pre wrap="">to
    </pre>
    <blockquote type="cite">
      <pre wrap="">fall through; otherwise  the compiler probably should fail
to compile
      </pre>
    </blockquote>
    <pre wrap="">(possibly
    </pre>
    <blockquote type="cite">
      <pre wrap="">switcheable off with a compiler option.)

e.g.

switch (foobar)
{
case 0:
dostuff();
nobreak; // we fall through, but that's fine.
case 1:
case 2: // note no nobreak required (although I wouldn't cry if it was)
domorestuff();
break;
default:
}

Anyway, just an idea; probably a very good one IMNHO though.

You might also consider the same idea in empty if and for statements.

-Ian
      </pre>
    </blockquote>
    <pre wrap="">    </pre>
  </blockquote>
  <pre wrap=""><!---->
  </pre>
</blockquote>
I agree that the c switch state is error prone.&nbsp; There has been quite a
bit of debate about changing the switch statement.&nbsp; However, your idea
seems fresh. <br>
I'd also be nice to do ranges on case statments such as 0..10, comment
separators and greater that/less then operators (&gt;=, &gt;, &lt;,
&lt;=).<br>
<br>
Anderson<br>
<br>
PS - Sorry Ian for posting to you, it's this new newsgroup program I'm
using, I'm not used to the position of the "send to newsgroup".
</div>
</body>
</html>
Sep 17 2003
prev sibling parent reply "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
Does anyone disagree that C/C++'s switch semantics are dangerous, even if
the (we) old farts have gotten used to it and rarely get bitten?

Does anyone think that it should be fixed in a way where implicit dangers
still lurk for the unwary, whether that be inability to grok the new
dangers, or the inability to get out of the mindset of the old (C/C++)
semantics?

Isn't the only sensible approach to have the compiler enforce the semantics,
and not allow any possibility of mistakes on our part?

Given that, why don't we simply accept the reality and have something along
the lines:

1. Each case must end in break, throw, return, fallthrough or goto
2. The default case must be included.

At first blush it seems nice to allow default to be skipped when switching
on an enum if all enum values are given cases. However, since they might be
updated in one bit of client code after the creation of the object code
containing the switch, this is probably not wise. Maybe this is where
Walter's exception would get thrown. But I think this stinks (and I'm not
alone), so I'd rather see either the default be required and the code
explicitly request that the exception be throw, or we could have another
case keyword unexpected


// 1, using default

int i

switch(i)
{
    case    1:
        func1();
        break;
    case    2:
        fallthrough: // I guess all you vowelly-challenged
western-hemisperians would probably go for fallthru here ... ;)
    case    3:
        func2_3();
        return;
    case    4:
        throw Some4Exception();
    case    5:
        goto label 5;
    default:
        break; // A null default
}


// 2, using unexpected

enum V
{
    a, b, c, d
}

V    v;

switch(v)
{
    case    a:
        func1();
        break;
    case    b:
        fallthrough: // I guess all you vowelly-challenged
western-hemisperians would probably go for fallthru here ... ;)
    case    c:
        func2_3();
        return;
    case    d:
        throw Some4Exception();
    unexpected:    // This doesn't have any contents (and it's an error to
have them), since it throws the exception that's currently thrown
}

Doesn't that cover every requirement that's been enunciated in this thread,
whilst preventing any implicit behaviour whatsoever. The cost to these huge
(IMO) benefits is that one must write default, or unexpected. Ouch! That's
really going to slow down development times!




"Ian Woollard" <Ian_member pathlink.com> wrote in message
news:bk5nri$14cm$1 digitaldaemon.com...
 Just had a quick look at the switch statement.

 I noticed the syntax is:

 switch (foobar)
 {
 case 0:
 dostuff();
 // I've forgotten to break; all is lost.
 case 1:
 domorestuffIdidntreallywantto();
 break;
 default:
 }

 This isn't such a good idea. The default behaviour is really unsafe. I

 suggest a 'nobreak' statement for cases where you really do want the code

 fall through; otherwise  the compiler probably should fail to compile

 switcheable off with a compiler option.)

 e.g.

 switch (foobar)
 {
 case 0:
 dostuff();
 nobreak; // we fall through, but that's fine.
 case 1:
 case 2: // note no nobreak required (although I wouldn't cry if it was)
 domorestuff();
 break;
 default:
 }

 Anyway, just an idea; probably a very good one IMNHO though.

 You might also consider the same idea in empty if and for statements.

 -Ian

Nov 28 2003
next sibling parent "Lars Ivar Igesund" <larsivar igesund.net> writes:
"Matthew Wilson" <matthew.hat stlsoft.dot.org> wrote in message
news:bq8dbs$it$1 digitaldaemon.com...
 , or we could have another
 case keyword unexpected

I like! Lars Ivar Igesund
Nov 29 2003
prev sibling parent reply Berin Loritsch <bloritsch d-haven.org> writes:
I think the principle of the easiest thing would mean that we have the compiler
require the "default" statement.  That would require the developer to think
about what will happen if something other than the expected happens.  It is
easier than finding out about it the hard way.

The important thing to realize is that D is the only language I am aware of
where the lack of a default clause to handle errors of this type will throw
an exception.  The number of people who will be caught by this trap is
staggering.

It's not just C/C++ developers.  There are also Objective-C, C#, Java, VB,
Perl, Python, etc.  Please, if it is an error not to handle all switch types,
require the developer to put in the default statement.

If nothing else, they can output some debug info to the std err stream.

Matthew Wilson wrote:

 Does anyone disagree that C/C++'s switch semantics are dangerous, even if
 the (we) old farts have gotten used to it and rarely get bitten?
 
 Does anyone think that it should be fixed in a way where implicit dangers
 still lurk for the unwary, whether that be inability to grok the new
 dangers, or the inability to get out of the mindset of the old (C/C++)
 semantics?
 
 Isn't the only sensible approach to have the compiler enforce the semantics,
 and not allow any possibility of mistakes on our part?
 
 Given that, why don't we simply accept the reality and have something along
 the lines:
 
 1. Each case must end in break, throw, return, fallthrough or goto
 2. The default case must be included.
 
 At first blush it seems nice to allow default to be skipped when switching
 on an enum if all enum values are given cases. However, since they might be
 updated in one bit of client code after the creation of the object code
 containing the switch, this is probably not wise. Maybe this is where
 Walter's exception would get thrown. But I think this stinks (and I'm not
 alone), so I'd rather see either the default be required and the code
 explicitly request that the exception be throw, or we could have another
 case keyword unexpected

<snip/>
Dec 01 2003
next sibling parent "Matthew Wilson" <matthew.hat stlsoft.dot.org> writes:
Hear, hear!

You've put it better than I did in any of my rants.

:)

"Berin Loritsch" <bloritsch d-haven.org> wrote in message
news:bqfjgb$10o9$1 digitaldaemon.com...
 I think the principle of the easiest thing would mean that we have the

 require the "default" statement.  That would require the developer to

 about what will happen if something other than the expected happens.  It

 easier than finding out about it the hard way.

 The important thing to realize is that D is the only language I am aware

 where the lack of a default clause to handle errors of this type will

 an exception.  The number of people who will be caught by this trap is
 staggering.

 It's not just C/C++ developers.  There are also Objective-C, C#, Java, VB,
 Perl, Python, etc.  Please, if it is an error not to handle all switch

 require the developer to put in the default statement.

 If nothing else, they can output some debug info to the std err stream.

 Matthew Wilson wrote:

 Does anyone disagree that C/C++'s switch semantics are dangerous, even


 the (we) old farts have gotten used to it and rarely get bitten?

 Does anyone think that it should be fixed in a way where implicit


 still lurk for the unwary, whether that be inability to grok the new
 dangers, or the inability to get out of the mindset of the old (C/C++)
 semantics?

 Isn't the only sensible approach to have the compiler enforce the


 and not allow any possibility of mistakes on our part?

 Given that, why don't we simply accept the reality and have something


 the lines:

 1. Each case must end in break, throw, return, fallthrough or goto
 2. The default case must be included.

 At first blush it seems nice to allow default to be skipped when


 on an enum if all enum values are given cases. However, since they might


 updated in one bit of client code after the creation of the object code
 containing the switch, this is probably not wise. Maybe this is where
 Walter's exception would get thrown. But I think this stinks (and I'm


 alone), so I'd rather see either the default be required and the code
 explicitly request that the exception be throw, or we could have another
 case keyword unexpected

<snip/>

Dec 01 2003
prev sibling parent Ant <Ant_member pathlink.com> writes:
In article <bqfjgb$10o9$1 digitaldaemon.com>, Berin Loritsch says...
I think the principle of the easiest thing would mean that we have the compiler
require the "default" statement.  

If nothing else, they can output some debug info to the std err stream.

Maybe the compiler could add that instead of throwing a fatal error. during compilation the module name and the line number are available. Ant
Dec 01 2003