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

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


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

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

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

<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);
>	}
>}
</snip>


September 18, 2003
>
> 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);
}


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


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

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
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.
>

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).


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