[comp.lang.c] Named Blocks

peter@thirdi.UUCP (Peter Rowell) (09/07/89)

In article <7598@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
...
>I just now produced one [a benign goto] ...
...
>  while(rule = (Rule*)Queue_iter_next(&rule_iter)) {
>    ...
>    while(rsym = (Symbol*)Queue_iter_next(&rsym_iter)) {
>    ...
>	switch (derives(rsym)) {
>	case derives_nothing:
>	  goto next_rule;
>	}
> ...
>    }
> ...
>  next_rule: continue;
>  }

I agree with David, since in C there really aren't any beautiful ways
to do the above.

One thing I would have loved to have seen introduced by ANSI would have
been "named blocks", but I am sure that parsing them is a bitch.
Borrowing heavily from SAIL, a named block could be defined as one that
has a string constant immediately following the opening curly brace.
The matching closing curly could have an optional string which, if
present, must match exactly the one attached to the opening brace.
This second part gives "begin/end" checking over arbitrarily nasty
nestings (like in the big switches often found in command interpreters,
etc.).  The name is available as an optional argument to "break" and
"continue".  If a name is missing, they (break/contiue) act as they do now.
If included, they allow multi-level break'ing and continue'ing.

Given this "improvement", the above might have been written:

    while(rule = (Rule*)Queue_iter_next(&rule_iter)) { "outer loop"
	while(rsym = (Symbol*)Queue_iter_next(&rsym_iter)) { "inner loop"
	    switch (derives(rsym)) { "switch 1"
		case derives_nothing:
		    continue "outer loop"; /* <<== next iteration of outer */
		case derives_something:
		    /* ... */
		    break;	/* a normal break */
		default:
		    break "inner loop";	/* <<== falls out the bottom of inner */
	    } "switch 1"
	} "inner loop"
	/* random code */
    } "outer loop"

which I believe is much more readbale.  Not all of the names were necessary,
but I was having fun.  Sigh, maybe we can do this in the next language.....

----------------------------------------------------------------------
Peter Rowell				peter@thirdi.UUCP
Third Eye Software, Inc.		(415) 321-0967
Menlo Park, CA  94025   	"There are already enough names." Lao Tze

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (09/07/89)

In article <475@thirdi.UUCP>, peter@thirdi.UUCP (Peter Rowell) writes:

|  One thing I would have loved to have seen introduced by ANSI would have
|  been "named blocks", but I am sure that parsing them is a bitch.

|  Given this "improvement", the above might have been written:
|  
|      while(rule = (Rule*)Queue_iter_next(&rule_iter)) { "outer loop"
|  	while(rsym = (Symbol*)Queue_iter_next(&rsym_iter)) { "inner loop"

  I think the idea of naming the loop rather than the block, as
suggested earlier, would be easier to parse.

Ex:
  while loop1 (a < b) {
    while (b = foo(2)) {
      /* stuff here */
      if (m > 20) continue loop1; /* specifies the scope of the loop */
    }
    /* this is where 'continue loop1' transfers */
  }

Disclamer: this is not my idea, and I am not offering an opinion on
including it in the language. I think discussion of these features is a
good way to have the major pitfalls discovered before someone has
implemented them.
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

bvs@light.uucp (Bakul Shah) (09/07/89)

In article <475@thirdi.UUCP> peter@thirdi.UUCP (Peter Rowell) writes:
>In article <7598@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>...
>>I just now produced one [a benign goto] ...
>...
>>  while(rule = (Rule*)Queue_iter_next(&rule_iter)) {
>>    ...
>>    while(rsym = (Symbol*)Queue_iter_next(&rsym_iter)) {
>>    ...
>>	switch (derives(rsym)) {
>>	case derives_nothing:
>>	  goto next_rule;
>>	}
>> ...
>>    }
>> ...
>>  next_rule: continue;
>>  }
>
>I agree with David, since in C there really aren't any beautiful ways
>to do the above.
>
>One thing I would have loved to have seen introduced by ANSI would have
>been "named blocks", but I am sure that parsing them is a bitch.
>
> [a version of the above example using named blocks]

Note that the `benign' goto above can be removed also by use of a
procedure.  It can also improve readability (which is the underlying
theme here).

    static foo(rule) {  /* rename `foo' to something better */
      ...
      while(rsym = (Symbol*)Queue_iter_next(&rsym_iter)) {
	...
	switch (derives(rsym)) {
	case derives_nothing:
	  return;
	}
      }

    caller_of_foo(..) {
      ...
      while(rule = (Rule*)Queue_iter_next(&rule_iter)) {
	...
	foo(rule);
      }
    }

I find short procedures and ones with simple control structues easy
to read.  But ones using nested loops and control jumps to various
levels of switches and loops are hard to grasp.  Breaking an inner
loop into a separate procedure (hopefully) forces one to think about
making the connection between the caller and callee relatively thin.

Also note that modern compilers are capable of inlining such one
time use procedures (with a little help from the user).

This is not to argue merits of named blocks but to point out that
existing features are not only capable of banishing gotos but can
also improve readability.

On a related note, one feature I miss in C is nested procedures.
Without them one ends up using global variables or passing lots of
arguments or avoids using a procedutre altogether because the code
that can be broken off into a separate procedure is just too tightly
coupled with its environment and would make for a very messy
interface (or force global var usage).  Oh well....

-- Bakul Shah <..!{ames,sun,ucbvax,uunet}!amdcad!light!bvs>

djones@megatest.UUCP (Dave Jones) (09/08/89)

From article <1989Sep7.162909.6638@light.uucp>, by bvs@light.uucp (Bakul Shah):

> 
> Note that the `benign' goto above can be removed also by use of a
> procedure.  It can also improve readability (which is the underlying
> theme here).

Just use a "return" to jump out of the procedure, rather than a "goto"
to jump out of the block. Sorry, but I don't see that as an improvement.

But there is a problem you seem to have overlooked. To keep the same
functionality, you are going to have to pass pointers to all local variables
into the newly created procedure, and replace all references to the local
procedures with dereferenced pointers. (C++ "reference" parameters would
save the day, but we're talking C, not C++)

A block of code such as the one under discussion simply does not belong in
another procedure, IMHO.

> 
> Also note that modern compilers are capable of inlining such one
> time use procedures (with a little help from the user).
> 

Name one C compiler that does that.

> This is not to argue merits of named blocks but to point out that
> existing features are not only capable of banishing gotos but can
> also improve readability.

We've known a long long time that gotos can be banished with existing
features. But is the cure worse than the complaint? I maintain that
my goto is benign. Creating a new procedure with scads of pointer
dereferences is major surgery.

> 
> On a related note, one feature I miss in C is nested procedures.
>

Sorry, we can't seem to agree on anything. "Nested procedures" is
just the automation of that pointer-creation/dereferencing I talked
about above. Usually there's one pointer for each lexical level,
(the "display"), and each nested procedure indexes off these for
all the "scoped" variables. Phooey on that. (Well, okay, since
we are now in the realm of what might be,  maybe one of
those "modern compilers" could inline the code when appropriate.)

> Without them one ends up using global variables or passing lots of
> arguments or avoids using a procedutre altogether because the code
> that can be broken off into a separate procedure is just too tightly
> coupled with its environment and would make for a very messy
> interface (or force global var usage).  Oh well....
> 

At last! Agreement.

This is indeed a problem. But it's a static problem. No need to
bring a runtime display into the picture.

One way to handle it might be a convenient macro mechanism:

      proc_which_would_otherwise_be_too_long()
      {
	int local_var;

        do_some_stuff();
	do_some_more_stuff();
      }

      where macro do_some_stuff() is
      { 
         local_var = 0;  /* Notice, it's still in scope */

         /* blah, blah.. */

         return;   /* We can debate as to whether this returns
                    * from the main procedure or from the macro.
                    * The macro, I think.
                    */
      }
      
It's sort of a cross between a macro and an inline procedure. It is
like a nested-scope procedure, except that it accesses its parent's
variables directly, not through a display, and of course, it can not
call itself or any other "macro" procedure.

Or, I could get by just fine with an editor that could name and
hide blocks, and store such info with the source files. (Hypertext?)
Then you could have this displayed...

      proc_which_would_otherwise_be_too_long()
      {
	int local_var; /* pooh on globals */

        <<do_some_stuff();>>
	<<do_some_more_stuff();>>
      }

If you clicked your mouse on the "do_some_stuff" area, the code would
be expanded there. Click in the expanded area again, and it hides
itself again. (If you work for one of those software companies, please
do not patent this idea. Thank you. I hearby place it in the public
domain.) This would work nicely in conjuction with the named blocks
idea.

ok@cs.mu.oz.au (Richard O'Keefe) (09/08/89)

In article <475@thirdi.UUCP>, peter@thirdi.UUCP (Peter Rowell) writes:
|  One thing I would have loved to have seen introduced by ANSI would have
|  been "named blocks", but I am sure that parsing them is a bitch.
In article <297@crdos1.crd.ge.COM>, davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) writes:
>  I think the idea of naming the loop rather than the block, as
>  suggested earlier, would be easier to parse.

I would point out that BCPL had named brackets $(NAMEHERE ..... $)NAMEHERE
long before C was thought of, and that the BCPL code for the front end of
the BCPL compiler is available in the book on BCPL.  Far from being
"a bitch" to parse, they are dead simple.  Just treat all brackets as
named with a possibly empty name, push the name onto a stack whenever you
see a "{...", and whenever you see a "}..." check for a match with the
top of the stack.

We can't use '{' "string" in an extension to C, because a C statement may
begin with a string.  For example
	"abc"[i] == ch ? foo() : baz();
Since C statements can't start with colons, perhaps
	<opening brace> ::= '{' ':' <identifier> | '{'
	<closing brace> ::= '}' ':' <identifier> | '}'
might do.

PL/I has named brackets where a labelled END statement may stand in for
any number of anonymous END statements as well; this is widely regarded
as a mistake.  Ada has loop labels (label:loop) and statement labels as
well (<<label>> statement).  

All things considered, might we not be better off with compilers that
can generate in-line code for nominated functions?

hascall@atanasoff.cs.iastate.edu (John Hascall) (09/08/89)

In article <7818@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
}From article <1989Sep7.162909.6638@light.uucp>, by bvs@light.uucp (Bakul Shah):
 
}> 
}> Also note that modern compilers are capable of inlining such one
}> time use procedures (with a little help from the user).
}> 
 
}Name one C compiler that does that.
 

     Ok.  The VAX/VMS compiler.  An excerpt from the help:

CC
 
  /OPTIMIZE[=option]
 
     Controls  whether  optimization  is  performed  by  the   compiler.
     /NOOPTIMIZE turns off the /PARALLEL qualifier.  The two options are
     as follows:                                                        

       ...

     [NO]INLINE     Specifies whether the compiler is allowed to perform
                    the function inline optimization.

John Hascall
ISU Comp Center

chris@mimsy.UUCP (Chris Torek) (09/08/89)

In article <7818@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
-Or, I could get by just fine with an editor that could name and
-hide blocks, and store such info with the source files. (Hypertext?)
-Then you could have this displayed...
-
-      proc_which_would_otherwise_be_too_long()
-      {
-	int local_var; /* pooh on globals */
-
-        <<do_some_stuff();>>
-	<<do_some_more_stuff();>>
-      }

Go to your nearby technical bookstore and look at copies of _TeX:_
_The_Program_ and _METAFONT:_The_Program_, by D. E. Knuth.

>If you clicked your mouse on the "do_some_stuff" area, the code would
>be expanded there. Click in the expanded area again, and it hides
>itself again. (If you work for one of those software companies, please
>do not patent this idea. Thank you. I hearby place it in the public
>domain.)

Too late :-)

WEB has other features that make it ugly, but this one in particular
is nice.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

merlyn@iwarp.intel.com (Randal Schwartz) (09/09/89)

In article <475@thirdi.UUCP>, peter@thirdi (Peter Rowell) writes:
| One thing I would have loved to have seen introduced by ANSI would have
| been "named blocks", but I am sure that parsing them is a bitch.
| Borrowing heavily from SAIL, a named block could be defined as one that
| has a string constant immediately following the opening curly brace.

Perl has 'em.

LINE: while ($_ = <stdin>) { # read a line into variable $_
	...;
	while ($something_needs_to_be_done) {
		...;
		...;
		next LINE if time > $quitting_time;
		# jump out to the outer loop
		...;
	}
	...;
}


Works pretty well.

Just another Perl hacker,
-- 
/== Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ====\
| on contract to Intel, Hillsboro, Oregon, USA                           |
| merlyn@iwarp.intel.com ...!uunet!iwarp.intel.com!merlyn	         |
\== Cute Quote: "Welcome to Oregon... Home of the California Raisins!" ==/