September 16, 2003
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
>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
>
>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
September 16, 2003
> >
> >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.
>

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
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 :^
> ~=  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
to do
> 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.


September 16, 2003
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
>do the
>> same thing, with one being used in tests and the other one not. That's
>just
>> 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
>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 :^
>> ~=  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
>to do
>> 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
September 17, 2003
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.
<convincing arguments snipped>

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

September 17, 2003
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

September 17, 2003
Don't know how parse text/html message
September 17, 2003
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
>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
>
>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();
>}
>
>


September 17, 2003
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.
September 17, 2003
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.

September 17, 2003
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.
><convincing arguments snipped>
> 
> 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