[comp.lang.c] The D Programming Language: cases

chris@trantor.umd.edu (Chris Torek) (03/03/88)

-In article <24996@cca.CCA.COM> g-rh@cca.CCA.COM (Richard Harter) writes:
-}As a side point, I like the suggestion that someone made that there be
-}a fallthrough statement rather than automatic fallthrough.

In article <222965b9@ralf.home> Ralf.Brown@B.GP.CS.CMU.EDU writes:
-So, you would like to have to write
-        switch(var)
-           {
-           case foo: fallthrough;
-           case bar: fallthrough;
-           case baz: fallthrough;
-           case mung: /* code to do something for all four cases */
-           }

While this would work, if we assume D has aggregate constructors,
there is a handsomer way:

	switch (e) {
	case [1, 2, 3, 5, 7, 13, 19, 23]: ...
	}
	/* syntax above is only for demonstration */
-- 
In-Real-Life: Chris Torek, Univ of MD Computer Science, +1 301 454 7163
(still on trantor.umd.edu because mimsy is not yet re-news-networked)
Domain: chris@mimsy.umd.edu		Path: ...!uunet!mimsy!chris

mlandau@bbn.com (Matt Landau) (03/03/88)

In comp.lang.c (<2403@umd5.umd.edu>), chris@trantor.umd.edu (Chris Torek) writes:
>While this would work, if we assume D has aggregate constructors,
>there is a handsomer way:
>
>	switch (e) {
>	case [1, 2, 3, 5, 7, 13, 19, 23]: ...
>	}
>	/* syntax above is only for demonstration */

And of course we add ranges, giving

	switch(e)
	{
	  case [1, 2, 3..7, 13, 19, 23..36]:
		.
		.
		.
	  default:
		.
		.
		.
	}
	/* what he said */

g-rh@cca.CCA.COM (Richard Harter) (03/03/88)

In article <2403@umd5.umd.edu> chris@trantor.umd.edu (Chris Torek) writes:
>-In article <24996@cca.CCA.COM> g-rh@cca.CCA.COM (Richard Harter) writes:
>-}As a side point, I like the suggestion that someone made that there be
>-}a fallthrough statement rather than automatic fallthrough.

>While this would work, if we assume D has aggregate constructors,
>there is a handsomer way:

>	switch (e) {
>	case [1, 2, 3, 5, 7, 13, 19, 23]: ...
>	}
>	/* syntax above is only for demonstration */

Well, yes, one ought to be able to do that.  However it is isn't quite
as strong as fallthrough, where one can say

	switch (e) {
	  case foo:
	    some code;
	    fallthrough;
	  case baz:
	    some more code;
	  }


In C as it stands now you can do this -- indeed, the complaint is that
one can do this unintentionally.  If one adds aggregate constructors and
takes away automatic fallthrough, it seems to me that you weaken the
language.   No doubt there are purists that say you shouldn't do the
sort of thing given above.  I wouldn't go that far, but I would agree
that one should be able to use aggregate constructors when cases actually
share code.

-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

pedersen@acf3.NYU.EDU (paul pedersen) (03/04/88)

In article <25200@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes:
>Well, yes, one ought to be able to do that.  However it is isn't quite
>as strong as fallthrough, where one can say
>
>	switch (e) {
>	  case foo:
>	    some code;
>	    fallthrough;
>	  case baz:
>	    some more code;
>	  }
>
>
>In C as it stands now you can do this -- indeed, the complaint is that
>one can do this unintentionally.  If one adds aggregate constructors and
>takes away automatic fallthrough, it seems to me that you weaken the
>language.   No doubt there are purists that say you shouldn't do the
>sort of thing given above.  I wouldn't go that far, but I would agree
>that one should be able to use aggregate constructors when cases actually
>share code.
>

Fallthrough may be occasionally justifiable.  One good example (stolen
from Knuth's classic article "Structured programming with go to statements")
is a P-code-type interpreter, where the Subtract case may negate one 
operand and then branch to the Add case.  I think that a better D option
would be to mark this explicitly with the classic language for marking
an "abnormal" transfer of control:

	switch (op) {
	case Add:
		blah-blah-blah
	case Subtract:
		negate operand;
		goto case Add;
	/* more cases */
		}

This 'goto' is signalled as exceptional by the reserved word 'case' appearing
in place of a label.  The use of 'goto' rather than 'fallthrough'
is also superior because it is not as fragile if a new case is introduced.

I do not think, however, that such a "general" concept as either fallthrough
or on-the-fly constructors should be  used to support the common
use of multiple cases which execute exactly the same code.
Here some other syntax is needed.  The natural 'case a,b,c:' is defeated
by C's comma operator, and 'case a:b:c:' is not LL(1) parseable, but
some other C-ish syntax can probably be invented.

In current practice, when I need to fall through, I always use the conventional
comment /* FALLTHROUGH */ to mark the spot.

cabo@tub.UUCP (Carsten Bormann) (03/06/88)

In article <25200@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes:
[about a solution to get back some expressive power that will be lost if
 ``case'' implies ``break'':]
() 	switch (e) {
() 	  case foo:
() 	    some code;
() 	    fallthrough;
() 	  case baz:
() 	    some more code;
() 	  }

(Current) C already has a good fallthrough statement.
It is called (surprise):

goto.

 	switch (e) {
 	  case foo:
 	    some code;
 	    goto baz_case;
 	  case baz:
	  baz_case:
 	    some more code;
 	  }

Any optimizer will throw away the superfluous branch implied by the goto.
This also has the advantage to use the existing ``spaghetti code warning''
keyword, and to allow you to give a descriptive name for the ``baz_case''.
Now if D had a comefrom...
-- 
Carsten Bormann, <cabo@tub.UUCP> <cabo@db0tui6.BITNET> <cabo@tub.BITNET>
Communications and Operating Systems Research Group
Technical University of Berlin (West, of course...)
Path: ...!pyramid!tub!cabo from the world, ...!unido!tub!cabo from Europe only.

g-rh@cca.CCA.COM (Richard Harter) (03/07/88)

In article <401@tub.UUCP> cabo@tub.UUCP (Carsten Bormann) writes:
>In article <25200@cca.CCA.COM> g-rh@CCA.CCA.COM.UUCP (Richard Harter) writes:
>[about a solution to get back some expressive power that will be lost if
> ``case'' implies ``break'':]
>
>(Current) C already has a good fallthrough statement.
>It is called (surprise):
>goto.

	[transferring between cases goto shown.]

	But, but, but... I had naturally supposed that the goto would be
dropped from D.  :-)

	Actually, does anyone use goto's in C to any signifigant extent?
Currently I average about one goto per 10,000 lines of code, all of them
being transfers to a procedure epilog, e.g

foobaz() {
  ....
  allocate space and other setup
  ....
  if (some_special_condition) goto wrapup;
  ....
wrapup:
  deallocate space and other cleanup
  }

	Almost every language has a goto construct, apparently on the
principle "We know you won't use goto's, but there might be a special
case where you need one, so here it is - better to be safe than sorry."
But do people actually use it to any signifigant extent?  Why not just
drop it?
-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

PEPRBV%CFAAMP.BITNET@husc6.harvard.EDU (Bob Babcock) (03/07/88)

>    Almost every language has a goto construct, apparently on the
>principle "We know you won't use goto's, but there might be a special
>case where you need one, so here it is - better to be safe than sorry."
>But do people actually use it to any significant extent?  Why not just
>drop it?

I don't understand.  First you give a perfectly reasonable example of
the use of goto (which would be awkward to code otherwise), and then
you propose eliminating goto from the language because you don't use
it very often.  One of the reasons I like programming in C rather than
(pre-77) Fortran is that you have the flow control constructs
necessary to eliminate abusive use of goto's, but you still have
goto available for the occasional place where it best expresses
what you want to do.

g-rh@cca.CCA.COM (Richard Harter) (03/07/88)

In article <12159@brl-adm.ARPA> PEPRBV%CFAAMP.BITNET@husc6.harvard.EDU (Bob Babcock) writes:

>I don't understand.  First you give a perfectly reasonable example of
>the use of goto (which would be awkward to code otherwise), and then
>you propose eliminating goto from the language because you don't use
>it very often.  One of the reasons I like programming in C rather than
>(pre-77) Fortran is that you have the flow control constructs
>necessary to eliminate abusive use of goto's, but you still have
>goto available for the occasional place where it best expresses
>what you want to do.

My apologies for muddying the waters.  Yes, there are rare occasions where
a goto lets you build by hand a construct that is not available in the
language.  Most of these instances reflect deficiencies in the language.
In the example I cited the deficiencies are:

(a)	C has two escape statements, break and return.  'return' escapes
from the outermost containing block (the function).  'break' is a little
ambiguous.  By analogy one would expect it to escape from the innermost
containing block.  Actually, of course, it escapes from loops and switch
control structures.  C would be improved if it had better escape constructs.

(b)	C has no provision for epilogs -- an epilog being a block of code
executed after an escape.  I don't quite see how one puts epilogs in C
in any natural fashion.

The question I was raising was whether the rare instances where a goto
is actually useful warrant retaining it in the language.  You and I, of
course, will use them wisely and only when warranted.  But I could do
without them in C.  The times that I would have to get around not having
them available are few compared to other awkwardnesses in the language.

Incidentally, the objection I have to older Fortran is not so much
the abusive use of goto's (a complaint I will leave to theoreticians)
but is their awkwardness -- expressing any kind of complicated logic
in Fortran 66 is rather like solving a chinese box puzzle.
-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

decot@hpisod2.HP.COM (Dave Decot) (03/07/88)

> The following example is illustrative syntax only:
> 
> [int *,int] foobar();
> ....
> [ptr,flag] = foobar(arg)
>    int arg;
> {
>    int *a, *b;
>    ....
>    return [a,1];
>    ....
>    return [b,0];
> }

How about this syntax:

    struct foo { int *x, y; } foobar();

    struct foo foobar(arg)
    int arg;
    {
	int *a, *b;

    ...
	return {a, 1};
    ...
	return {b, 0};
    }

This way, all you're adding to the existing language is aggregate expressions.

Dave Decot
hpda!decot

gregg@a.cs.okstate.edu (Gregg Wonderly) (03/08/88)

From article <25200@cca.CCA.COM>, by g-rh@cca.CCA.COM (Richard Harter):
> In article <2403@umd5.umd.edu> chris@trantor.umd.edu (Chris Torek) writes:
>>-In article <24996@cca.CCA.COM> g-rh@cca.CCA.COM (Richard Harter) writes:
>>-}As a side point, I like the suggestion that someone made that there be
>>-}a fallthrough statement rather than automatic fallthrough.
> 
>>While this would work, if we assume D has aggregate constructors,
>>there is a handsomer way:
> 
>>	switch (e) {
>>	case [1, 2, 3, 5, 7, 13, 19, 23]: ...
>>	}
>>	/* syntax above is only for demonstration */
> 
> Well, yes, one ought to be able to do that.  However it is isn't quite
> as strong as fallthrough, where one can say
> 
> 	switch (e) {
> 	  case foo:
> 	    some code;
> 	    fallthrough;
> 	  case baz:
> 	    some more code;
> 	  }
> 
> 
> In C as it stands now you can do this -- indeed, the complaint is that
> one can do this unintentionally.  If one adds aggregate constructors and
> takes away automatic fallthrough, it seems to me that you weaken the
> language.   No doubt there are purists that say you shouldn't do the
> sort of thing given above.  I wouldn't go that far, but I would agree
> that one should be able to use aggregate constructors when cases actually
> share code.

This is just one of the many things that people gripe and complain about
when writing C.  In my eyes it comes down to poor/bad (which is your favorite?)
program writing practices.  You can eliminate the majority of the problems
associated with `break' in a switch by prototyping the entire switch before
filling in the code (you mean you are coding off the top of your head :-).
e.g.

	switch (val) {
		case 'a':
		case 'b':
			break;
		case 'c':
			break;
		case 'f':
		case 'h':
		case 'v':
			break;
		default:
			wrong_again_honey();
	}

Then you can add the code for each case by opening a line above the `break'
statements.  If you know that you need a switch, you should know what the
structure of it will be initially (prior to adding that other feature), so
just type it (you know, like stubbing out a program).  While I am at it,
one of my biggest gripes is that too many people exploit C's idea of a 
statement so that they can write

	if (some_expression)
		for (init; continuation_condition; repeat_code)
			switch (val) {
				forty jillion case's
			}

without placing braces around the `switch' and the `for'.  I guess these
people either don't use VI, or haven't got a bracket matcher in their
favorite editor.  This kind of code is terribly difficult to get through.
I prefer...

	if (some_expression) {
		for (init; continuation_condition; repeat_code) {
			switch (val) {
				forty jillion case's
			}
		}
	}

Then there are those people who lost sight of their space bar and insist
on typing things like,

	for(a=4,b=5,s=&c;((a<b&&d>>4)+(int)s+1)!=c<d;++a,--d,c=a)

or some such arcane expression without a single space to try and make the
code even slightly more readable.

Well, I will stop here, as programming style is kind of like editors and
religion.  Everybody has there own, and nobody can make them change... sigh!!!

-----
Gregg Wonderly
Department of Computing and Information Sciences
Oklahoma State University

UUCP:      {cbosgd, ihnp4, rutgers}!okstate!gregg
Internet:  gregg@A.CS.OKSTATE.EDU

karl@haddock.ISC.COM (Karl Heuer) (03/09/88)

In article <562@acf3.NYU.EDU> pedersen@acf3.UUCP (paul pedersen) writes:
>Fallthrough may be occasionally justifiable.  [As in case Subtract doing a
>negate and then falling into case Add.]

Of course, even C-like fallthrough isn't good enough if you have THREE cases
that end up at the same point.  (Assuming the code to be executed is AC | BC |
C, not ABC | BC | C.)  Or even two cases with different initial segments (AC |
BC).  Might just as well use the same construct (goto) for all of these.  (But
I do like `goto case X` and `goto default`; one ought to be able to use the
existing label.)

>I do not think, however, that such a "general" concept as either fallthrough
>or on-the-fly constructors should be used to support the common use of
>multiple cases which execute exactly the same code.  Here some other syntax
>is needed.

I agree.

>The natural 'case a,b,c:' is defeated by C's comma operator,

That's no worse than the other comma conflicts.  If the comma operator is
still illegal in constant expressions (it was in an early Draft; I haven't
checked the latest one), this syntax could be added to C with no conflicts.
If not, then could add it anyway, and let it override the comma operator just
as `f(x,y)` does.  (If for any reason you actually need a comma operator in a
case statement, you write `case (A,B,C):`.)  If we're talking D rather than C,
we don't even have to worry about breaking existing code.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

jbn@glacier.STANFORD.EDU (John B. Nagle) (03/09/88)

     Provided that goto operations are limited to forward and outward 
transfers of control, they are relatively harmless, although they do
make flow analysis within an optimizing compiler more difficult.

g-rh@cca.CCA.COM (Richard Harter) (03/09/88)

>
>Of course, even C-like fallthrough isn't good enough if you have THREE cases
>that end up at the same point.  (Assuming the code to be executed is AC | BC |
>C, not ABC | BC | C.)  Or even two cases with different initial segments (AC |
>BC).  Might just as well use the same construct (goto) for all of these.  (But
>I do like `goto case X` and `goto default`; one ought to be able to use the
>existing label.)

A more general solution runs as follows:  Associate a flag with each such block.Set the execution control flags in the switch case blocks; execute the blocks
under if control.  E.g.

ex_A = ex_B = ex_C = 0;
switch (foo) {
  case 0: ex_A = ex_C = 1; break;
  case 1: ex_B = ex_C = 1; break;
  case 2: ex_C = 1; break;
  default: break;
  }
if (ex_A) {A}
if (ex_B) {B}
if (ex_C) {C}

This works quite nicely if the blocks are to be sequentially executed.
In the general case more than one block may be executed per case, in
varying orders, and the order is affected by results within blocks, I
would much rather use state machine logic and transition tables than
a rats nest of goto's.  This is a personal taste, no doubt, but I find
it is easier to construct and maintain the tables, then it is to go into
the code and fiddle with the logic.
-- 

In the fields of Hell where the grass grows high
Are the graves of dreams allowed to die.
	Richard Harter, SMDS  Inc.

terry@wsccs.UUCP (terry) (03/20/88)

	In article <24996@cca.CCA.COM> Richard Harter writes:
	As a side point, I like the suggestion that someone made that there be
	a fallthrough statement rather than automatic fallthrough.

In a later article, Richard gives an example of his proposed syntactic change,
in response to suggestion that aggregate constructors would not be as strong as
the current language:
	
	From article <25200@cca.CCA.COM>, Richard Harter:
	 Well, yes, one ought to be able to do that.  However it is isn't quite
	 as strong as fallthrough, where one can say

	 	switch (e) {
	 	  case foo:
	 	    some code;
	 	    fallthrough;
	 	  case baz:
	 	    some more code;
	 	  }

I think that this would be dangerous, in that it would have further reaching
effects than Richard anticipates.  Since the discussion centers around 'D',
which by context is simply "a new standard C which may not be compatable with
the current K&R standard of C so we'll call it 'D'", I believe that "upward"
compatability must be maintained if all C software is to be recompiled in D
with minimal tweaking.  While a change this drastic _could_ be worked around
with something as simple as

	#define case	fallthrough; case

this does _not_ help code generators such as YACC and LEX generate this new
code.  There are quite a number of developement tools which would have to
either be sacraficed or rewritten if D is to become the new standard.  While
this will necessary if _any_ changes are made from the current C definition,
these changes should, as a matter of course, be minimized.  The alternative
is a new language which will require ALL new tools, something I think a great
deal of people would rather avoid. 

Chris' example of aggregate constructors was:

	In article <2403@umd5.umd.edu> Chris Torek:

	While this would work, if we assume D has aggregate constructors,
	there is a handsomer way:
 
		switch (e) {
		case [1, 2, 3, 5, 7, 13, 19, 23]: ...
		}
		/* syntax above is only for demonstration */

Given that the syntax is only for demonstration, I would suggest the following
as a better (more backward compatable) method:

		switch( e) {
		case 1,2,3,4:
		}

While apparently at conflict with the comma operator, one should realize that
the compiler expects a constant, not an expression, following a case statement,
so there would be no ambiguity in parsing.


In article <3278@okstate.UUCP>, Gregg Wonderly has suggested that the whole
complaint centers on poor programming practices:

	You can eliminate the majority of the problems associated with `break'
	in a switch by prototyping the entire switch before filling in the code
	(you mean you are coding off the top of your head :-).

I don't think this is entirely true.  If you are a paper-programmer (one that
writes code on paper, and when it's "right", types it in), this is probably
what you'd think.  A great deal of programmers (myself included) DO program
off the tops of our heads, when the job is sufficiently simple.  In addition,
when it isn't, I use a tool called an editor ;-).  With it, I can move case
statements around to the point that I break them :-(.  This type of breakage
would not be helped by prototyping, but WOULD be caught if 'fallthrough' were
mandatory, my workaround withstanding.  A 'fallthrough' statement would save
a careless programmer (Yech! 11 characters!), but I still think it'll make
"upgrading" previous code *much* more difficult.  Besides, I never make that
kind of mistake ;-).


Gregg also complains of a style issue, particularly, leaving the braces off
of the 'if' and 'for' in the following example:

 	if (some_expression)
 		for (init; continuation_condition; repeat_code)
 			switch (val) {
 				forty jillion case's
 			}
 
complaining that it is hard to get through and find the end of the statement.
Why not use the '{' following the switch?  Indentation is part of style, too.


| Terry Lambert           UUCP: ...{ decvax, ihnp4 }                          |
| @ Century Software          : ...utah-cs!uplherc!sp7040!obie!wsccs!terry    |
| SLC, Utah                                                                   |
|                   These opinions are not my companies, but if you find them |
|                   useful, send a $20.00 donation to Brisbane Australia...   |

chris@mimsy.UUCP (Chris Torek) (03/23/88)

In article <336@wsccs.UUCP> terry@wsccs.UUCP (terry) writes:
>... Since the discussion centers around 'D', which by context is
>simply "a new standard C which may not be compatable with
>the current K&R standard of C so we'll call it 'D'",

That is not what *I* mean by `D'.  I thought we were discussing a
language that would be a replacement for C as a systems and/or
general-purpose programming language.

>I believe that "upward" compatability must be maintained if all C
>software is to be recompiled in D with minimal tweaking.

I had in mind more of an automatic translator (in which inserting
`fallthrough' statements would be trivial).  Of course this is largely
just an intellectual exercise anyway (and, some say, a light workout
at that :-) ).

>While a change this drastic [requiring `fallthrough' for case fallthrough]
>_could_ be worked around with something as simple as
>
>	#define case	fallthrough; case
>
>this does _not_ help code generators such as YACC and LEX generate this new
>code.

Actually, as it happens, both yacc and lex always generate the `break'
for you.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

karl@haddock.ISC.COM (Karl Heuer) (03/23/88)

In article <336@wsccs.UUCP> terry@wsccs.UUCP (terry) writes:
>I think that [an explicit fallthrough statement] would be dangerous, in that
>it would have further reaching effects than Richard anticipates.  Since the
>discussion centers around 'D', which by context is simply "a new standard C
>which may not be compatable with the current K&R standard of C so we'll call
>it 'D'", I believe that "upward" compatability must be maintained if all C
>software is to be recompiled in D with minimal tweaking.

That sounds more like a description of ANSI C.  "D" is intended to be a
language which is as powerful as C but (unlike ANSI C and C++) is *not bound
at all* by the compatibility requirement.  (Although a C-to-D translation tool
would probably appear, in the unlikely event that this thought experiment
actually materializes into something tangible.)

>While a change this drastic _could_ be worked around with something as simple
>as    #define case fallthrough; case    this does _not_ help code generators
>such as YACC and LEX generate this new code.

What's the problem?  I think yacc currently generates a break after each case
block; the solution then is to omit this if generating D.  Even if yacc does
depend on the current automatic fall-through semantics, it could generate an
explicit "fallthrough;" statement at the bottom of each case block.  Surely it
has enough information for this -- I'd bet it's a one-line change.

(Btw, although I would prefer a "fallthrough" statement to the current C
semantics, I think that both are inferior to "goto case N".)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

rcvie@tuvie (ELIN Forsch.z.) (03/23/88)

In article <336@wsccs.UUCP< terry@wsccs.UUCP (terry) writes:
<Given that the syntax is only for demonstration, I would suggest the following
<as a better (more backward compatable) method:
<
<		switch( e) {
<		case 1,2,3,4:
<		}
<
<While apparently at conflict with the comma operator, one should realize that
<the compiler expects a constant, not an expression, following a case statement,
<so there would be no ambiguity in parsing.
<

Just spell it correct. The compiler expects neither a `constant' nor an
`expression' but a `constant expression'. As, however, the comma operator
must not be part of a constant expression (as far as my dpANS-edition says)
there still is no ambiguity. But this restriction is still discussed.

		Dietmar Weickert,
			ALCATEL-ELIN Research Center, Vienna, Austria.