[comp.object] code blocks

delft@fwi.uva.nl (Andre van Delft) (06/29/90)

John Maloney writes:
>In reply to Brad Cox's statement:
>> what about a C-based varient of Smalltalk Blocks?
>
>I think that blocks (or "closures", to be technical) are an extremely
>valuable language mechanism. They allow all sorts of customized control
>structures to be synthesized without extending the language semantics at all.

How would code blocks in C look like?
A code block should specify its type and its formal parameters.
The syntax of a normal function declaration without the function identifier
may be appropriate, as in

   aFunctioncall (1, void(){someCode;} )

A problem is in the typeident "void"; when enclosed in parentheses

   aFunctioncall (1, (void)(){someCode;} )

the compiler will see "(void)" as casting.

Any better ideas?

cox@stpstn.UUCP (Brad Cox) (07/01/90)

In article <1112@carol.fwi.uva.nl> delft@fwi.uva.nl (Andre van Delft) writes:
:John Maloney writes:
:How would code blocks in C look like?

No, a block (I prefer the word 'action') should be an *expression*, not 
a *statement*. The following syntax might suffice (assuming that C 
compound statement syntax can be parsed specially to denote block 
instantion when used as an expression).

	id aLocalVariable;
	aBlock = { action for subsequent invocation, which might and often will
		reference variables like aLocalVariable, formal arguments, etc. };
	[aMenu selectAction:aBlock];

or

	[aCollection do:{ id blockFormalArgument; | 
		[blockFormalArgument someOperation]; }];

or

	[ { action to run concurrently} fork];

or
	[ { action that might fail } with:{ action for handling exceptions}];

The main thing that makes blocks nontrivial is references from the block
into the instantation-site's local variables.
-- 

Brad Cox; cox@stepstone.com; CI$ 71230,647; 203 426 1875
The Stepstone Corporation; 75 Glen Road; Sandy Hook CT 06482

ark@alice.UUCP (Andrew Koenig) (07/02/90)

There are three problems in extending a language like C to
support closures (Smalltalk blocks, Lisp lambda expressions
in Lisps with lexical scoping, etc).


The first problem is that the calling sequence has to be changed
so that a function always has available the location of the appropriate
stack frame of the lexically surrounding scope.  For example
(and I'm going to keep these examples in C rather than C++ to
show that they are universally applicable), here's a function that
applies a given function to each element of an integer array:

	void apply(int *a, int n, void f(int))
	{
		int i;
		for (i = 0; i < n; i++)
			f(a[i]);
	}

Before you flame me for writing `void f(int)' instead of `void (*f)(int),'
note that that is an acceptable abbreviation in ANSI C.

Now, let's use the `apply' function to add up the elements of an array:

	static int sum;
	static void add(int i) { sum += i; }

	int sigma(int *a, int n)
	{
		sum = 0;
		apply(a, n, add);
		return sum;
	}

This solution is unsatisfying because it uses a global variable.
Even though I can sort of hide that global variable by declaring
it `static,' I'm sure you can see that its mere existence will
cause trouble in more complicated contexts.  For example, a similar
program intended to add all the elements in a tree would have to do
so by having the equivalent of the `add' function make recursive
calls on the `sigma' function; this wouldn't work as shown here
because sigma uses a static accumulator.

What we really want is the ability to make `sum' local to `sigma,'
which also requires making `add' local to `sigma:'

	int sigma(int *a, int n)
	{
		int sum = 0;
		void add(int i) { sum += i; }
		apply(a, n, add);
		return sum;
	}

This is precisely the thing that most present C implementations cannot
do.  For the program above to work, it is necessary for `add' to be
able to get at the corresponding stack frame for `sigma.'  Moreover,
there may be more than one stack frame in existence, so some kind of
bookkeeping is necessary to determine the right one.

This problem is well known, of course, and lots of languages have
solved it, but Dennis Ritchie made a conscious decision not to
impose the associated overhead on C and that decision has stuck.

Anyway, suppose we had an extended C that allowed this kind of thing.
The next step would be to realize that there is really no need
to give a name to `add' at all -- in principle one could include it
directly in the argument list for `apply:'

	int sigma(int *a, int n)
	{
		int sum = 0;
		apply(a, n, void(int i) { sum += i; });
		return sum;
	}

I am ignoring the likely parsing ambiguities that stem from this
particular notation; I am sure they can be worked out and I have heard
(though am not actually familiar with the details) about one C compiler
that has implemented just this extension.


The second problem is that when one has local functions, it is tempting
to say that one should be allowed to return them as values of functions.

For example, our `sigma' function is conceptually bound to the notion
of addition and an initial value of zero.  Suppose we made those both
parameters?

	int reduce(int start, int accum(int, int))(int *, int)
	{
		int f(int *a, int n) {
			int ac = start;
			int i;
			for (i = 0; i < n; i++)
				acc = accum(acc, a[i]);
			return acc;
		}
		return f;
	}

We now have a function called `reduce' that takes a starting value
and an accumulation function and returns a function that operates
on an integer array.  One might use it this way:

	int (*sigma)(int*, int) =
		reduce(0, int(int x, int y) { return x + y; });
	int (*pi)(int*, int) =
		reduce(1, int(int x, int y) { return x * y; });

Here, then we use the function `reduce' to create the two functions
we subsequently refer to as sigma and pi.

People who have seen this style or programming before probably realize
how much expressive power there is to be had thereby.  Unfortunately,
implementing it requies powerful run-time mechanisms -- probably the
equivalent of a general-purpose garbage collector -- and is therefore
very hard to add to C because of C's unrestricted use of pointers.


The third problem is that even if it were possible to implement these
extensions to C with no problems, it wouldn't do me any good unless
all my potential customers had access to those implementations.
It doesn't matter how powerful a language is if my programs can't
be used by the people who have to use them for them to be useful.
If I am programming only for myself, I can use any tools I have at
my disposal.  But if I'm trying to write stuff for others to use,
I must either use tools that are compatible with theirs or make my
tools available to them.  This problem, and the necessity for its
solution, often far outweighs any technical argumens about the tools
in question.
-- 
				--Andrew Koenig
				  ark@europa.att.com

deutsch@parcplace.com (Peter Deutsch) (07/03/90)

Andrew Koenig's post did a nice job of demonstrating the value and power of
closures.  However, I find I disagree with his analysis of why they are not
appropriate for C or C++.

C and C++ already take the point of view that one can create, and freely
pass around, pointers to locally allocated variables, despite the fact that
these pointers become invalid when one leaves the dynamic scope in which
the variable was allocated.  Adding closures would not create qualitatively
new problems of this kind: they would simply be another case of an existing
problem.  If the former problem isn't sufficient to motivate a requirement
for a garbage collector, I don't think the latter is either.

Closures can be implemented with absolutely no overhead for places that
don't use them, and relatively low overhead for places that DO use them.  I
think the trick I'm about to describe was originally invented at Xerox
PARC; at least, that's where I remember first hearing it.  The
representation of a closure is a pointer to a small block of code stored in
the enclosing stack frame.  This code pushes an extra argument -- the
address of the frame itself -- and then jumps to the actual procedure.  The
inner procedure thus takes an additional implicit argument, which is the
address of the correct dynamic instance of the statically enclosing frame.
This can obviously be iterated to handle multiple levels of inner
procedure.  It requires a small amount of cooperation from the operating
system and/or the machine architecture, since it requires the ability to
construct code on the fly and hence the ability to properly synchronize the
instruction and data caches.

Using this scheme, a call through a procedure variable is always simply an
indirect call.  Procedure variables still only occupy the same amount of
space as any other kind of pointer.

Objectworks for Smalltalk-80 happens not to implement closures this way,
because its fundamental operation is method invocation, not procedure call.
However, I can imagine using a scheme (:-)) like this for Lisp.

jmaloney@cs.washington.edu (John Maloney) (07/03/90)

Brad Cox and Andrew Koenig's messages nicely summarize both
the motivation for and the implementation problems with adding
closures ("blocks") to a language like C++ or Objective-C. He
points out that the implementation problems are deep because:

    full closures require dynamically allocated variable contexts,
    a garbage collector is required to get rid of these contexts when
        they are no longer referenced, and
    adding garbage collection to C is hard because it is difficult
        for the runtime system to know for certain which variables
        contain pointers and which contain integers.

Syntax and parsing issues are relatively unimportant compared
to these implementation problems. I still maintain that closures
are a powerful and useful language feature and think they
should stay on the "wish list". Of course, I also believe that
garbage collection is well-worth the 10% overhead that a good
incremental garbage collector consumes. Programmers' time
and program correctness are far more  important than a
program than runs a mere 10% faster.

	-- John

lgm@cbnewsc.att.com (lawrence.g.mayka) (07/03/90)

In article <DEUTSCH.90Jul2150016@atlantic.parcplace.com> deutsch@parcplace.com (Peter Deutsch) writes:
>C and C++ already take the point of view that one can create, and freely
>pass around, pointers to locally allocated variables, despite the fact that
>these pointers become invalid when one leaves the dynamic scope in which
>the variable was allocated.  Adding closures would not create qualitatively
>new problems of this kind: they would simply be another case of an existing
>problem.  If the former problem isn't sufficient to motivate a requirement
>for a garbage collector, I don't think the latter is either.

A closure which remains valid *even after the return of its
enclosing function* is qualitatively different from the current C
and C++ treatment of pointers to local variables.  A better analog
would be C and C++  malloc  and  new , which generally require
explicit  free  and  delete , respectively.

>PARC; at least, that's where I remember first hearing it.  The
>representation of a closure is a pointer to a small block of code stored in
>the enclosing stack frame.  This code pushes an extra argument -- the

Such a closure would remain valid only as long as the enclosing
stack frame remains valid.

>However, I can imagine using a scheme (:-)) like this for Lisp.

Common Lisp supports closures with indefinite lifetimes, and which
are therefore garbage-collected.


	Lawrence G. Mayka
	AT&T Bell Laboratories
	lgm@iexist.att.com

Standard disclaimer.

cox@stpstn.UUCP (Brad Cox) (07/03/90)

In article <11002@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
>The first problem is that the calling sequence has to be changed
>so that a function always has available the location of the appropriate
>stack frame of the lexically surrounding scope.

Andrew, thanks for your insightful analysis of the issues surrounding
the addition of Smalltalk Blocks, Closures, or Actions to C. In my
article which triggered this topic, I was referring to a much simpler
and less powerful scheme that can be implemented on vanilla C by not
duplicating Smalltalk continuations exactly. As you point out, maintaining
the location of the stack frame that instantiated the block is intractible
because of C's stack management philosophy (however see below). My
(simplified, not as powerful) scheme avoids this by simply evaluating
local scope variables at block *instantiation* time, rather than block
*invocation* time as in Smalltalk. At instantiation time, the arguments
are still in scope, and can be simply copied and held for future use
as instance variables of the block that is being instantiated.

As for doing the job right, there are intriguing claims in an article in
the ACM SigPlan 90 Conference on Programming Language Design and 
Implementation, "Representing Control in the Presence of First-class 
Continuations", by Robert Hieb et all of Indiana University. It 
describes a stack management philosophy that is claimed to be <faster than?
as fast as? almost as fast as? within a constant factor of? within an order
of magnitude of?> C's traditional approach.

The article is quite readable, and I believe compelling.  Although I'm
aware of the difficulty of adopting a new stack management paradigm within
C, I'm also a believer that digging our way out of the software crisis 
will *require* even higher level styles of object-oriented programming
than we've seen in either C++ (gate/block level objects) or Objective-C 
(chip-level objects).

I'm particularly enamored of coroutine-based (card-level) objects as
in LabView, Metaphor, or Fabrik, since by supporting an OO paradigm that
even non-programmers can understand, we might solve the programmer shortage 
as the telephone operator shortage was once solved, by turning *everyone* 
into programmers. But this will eventually require doing something about
C's substructure, whereas Bjarne and I have been living with that and
building superstructures.
-- 

Brad Cox; cox@stepstone.com; CI$ 71230,647; 203 426 1875
The Stepstone Corporation; 75 Glen Road; Sandy Hook CT 06482

ark@alice.UUCP (Andrew Koenig) (07/04/90)

In article <DEUTSCH.90Jul2150016@atlantic.parcplace.com>, deutsch@parcplace.com (Peter Deutsch) writes:

> Andrew Koenig's post did a nice job of demonstrating the value and power of
> closures.  However, I find I disagree with his analysis of why they are not
> appropriate for C or C++.

Actually, I stopped just short of saying they were inappropriate.
They are problematic, though, and I think we agree on some of the
reasons why.  Algol 68 has similar problems, if I remember correctly.

> Closures can be implemented with absolutely no overhead for places that
> don't use them, and relatively low overhead for places that DO use them.

I won't repeat your description of the technique here, but I will say
that it looks like a nice one.  It does have one disadvantage,
and that is that there are some machines that do not allow the
program counter to point into data space.  Also, it is presumably
necessary to create these little pieces of code for those stack
frames that need them.
-- 
				--Andrew Koenig
				  ark@europa.att.com

bs@alice.UUCP (Bjarne Stroustrup) (07/05/90)

Software is not hardware. Further, there is little reason to believe
that software is analogous to hardware in any strong and useful sense;
had it been, we probably wouldn't have had a `software crisis.' Looking
to hardware for solutions to software problems (except by using better
hardware) is likely to be highly misleading and causes much confusion.

I will happily agree that C++ isn't going to `solve the software crisis;'
and hurry to add that neither is any other language/environment.

The problems with software are fundamental. They are primarily caused by
increasing ambition on the part of individuals and organizations: As long
as we are seeing rapid progress many of us will be working at the hairy
edge of our abilities and of the capabilities of our tools. I hope and
expect that this trend will continue. Our tools are quite adequate for
solving yesterday's problems - meaning that we have made significant
progress - but those are not the problems we are facing today or want
to face tomorrow.

The software crisis has been with us for at least 25 years and will probably
be with us for at least another 25. Whether C++ or Objective C or something
else provides and/or will provide the greatest amount of relief from our
current problems and leverage for our new projects is for the users to decide.

davidc@vlsisj.VLSI.COM (David Chapman) (07/06/90)

In article <11013@alice.UUCP> bs@alice.UUCP (Bjarne Stroustrup) writes:
>Software is not hardware. Further, there is little reason to believe
>that software is analogous to hardware in any strong and useful sense;
>had it been, we probably wouldn't have had a `software crisis.' Looking
>to hardware for solutions to software problems (except by using better
>hardware) is likely to be highly misleading and causes much confusion.

I believe exactly the opposite:  that software is exactly analogous to
hardware.  It's just that the software is one or two orders of magnitude
more complex at any given point in time.

We write software to help system designers build ICs.  If it weren't for
CAD software from us, other companies, and universities, there wouldn't
be enough chip designers to go around.

I do agree that we cannot look to hardware for solutions to software
problems.  If anything it's the other way around.  We use software to
build hardware.  Eventually you may be able to write code and have your
CAD software automatically generate a chip for you that executes the
code directly.

>The problems with software are fundamental. They are primarily caused by
>increasing ambition on the part of individuals and organizations: As long
>as we are seeing rapid progress many of us will be working at the hairy
>edge of our abilities and of the capabilities of our tools.

We see exactly this problem with chip designers.  There's just an extra limit:
die size (corresponding to number of lines of code).  It can't be too big
or else it is physically unmanufacturable.  The Motorola 68000, with about 
68,000 transistors, is now a small design.  We are now building half-million 
transistor custom ICs.

And I find I design hardware the same way I write software.  A NAND gate is
in some sense the Turing machine of logic designers.  After all, our software
runs on hardware.  Why shouldn't they be similar at the most fundamental
levels?
-- 
		David Chapman

{known world}!decwrl!vlsisj!fndry!davidc
vlsisj!fndry!davidc@decwrl.dec.com

cox@stpstn.UUCP (Brad Cox) (07/07/90)

[Apologies if this was transmitted twice. We've been having networking
problems and I'm not sure if my first try succeeded].

In article <11002@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
:There are three problems in extending a language like C to
:support closures (Smalltalk blocks, Lisp lambda expressions
:in Lisps with lexical scoping, etc).

:I think we can all forget about translating this kind of thing to
:C, at least for the time being.

The article, "Representing Control in the Presence of First-class 
Continuations, (Hieb et all; Proceedings of the Sigplan90 Conference 
on Programming Language Design and Implementation, November 1990)
makes some fascinating claims that you should be aware of, roughly
to the effect that a segmented stack architecture could support
full-scale continuations in C without (any? much?) degradation over
C's current approach.

Although I'm keenly aware of the difficulties of pulling off what 
would probably involve a new code generation approach underneath C, 
as opposed to building new superstructures on top. But some of the 
higher-level superstructures, higher even than C++ (gate/block level) 
or Objective-C (chip level), particularly card-level systems like 
Fabrik, Metaphor, or LabView, may not be possible to do in fully robust 
form without doing something new at the foundation level.
-- 

Brad Cox; cox@stepstone.com; CI$ 71230,647; 203 426 1875
The Stepstone Corporation; 75 Glen Road; Sandy Hook CT 06482

cox@stpstn.UUCP (Brad Cox) (07/07/90)

In article <11013@alice.UUCP> bs@alice.UUCP (Bjarne Stroustrup) writes:
>
>Software is not hardware. Further, there is little reason to believe
>that software is analogous to hardware in any strong and useful sense;
>had it been, we probably wouldn't have had a `software crisis.' Looking
>to hardware for solutions to software problems (except by using better
>hardware) is likely to be highly misleading and causes much confusion.

No, but software people are people, different in no important respect
from those who built the pyramids, fly to the moon, build computer
hardware, and repair their plumbing.  We're missing a great opportunity
and remain stuck in the software crisis so long as we continue to
emphasize the uniqueness of our materials at the expense of well-proven
organizational prinicples such as division of labor, interchangeability,
and the commercial incentives required to make any large-scale cooperative
approach work, be it for hardware components, pyramid-building, or
software development.

>I will happily agree that C++ isn't going to `solve the software crisis;'
>and hurry to add that neither is any other language/environment.

The silver bullet is not a technology, but a paradigm shift. It is a
*cultural* change, not a technological one, in precisely the sense that
Copernicus, Kepler and others forged a silver bullet for the astronomy
crisis. It was not some supercomputer or programming language for computing
epicycles more rapidly. It was a shift in that culture's viewpoint in 
which the astronomers realized that they (and the earth) were not the 
center of the universe, but a cog in a larger wheel circling round the sun.

>The problems with software are fundamental. They are primarily caused by
>increasing ambition on the part of individuals and organizations: As long
>as we are seeing rapid progress many of us will be working at the hairy
>edge of our abilities and of the capabilities of our tools. I hope and
>expect that this trend will continue. Our tools are quite adequate for
>solving yesterday's problems - meaning that we have made significant
>progress - but those are not the problems we are facing today or want
>to face tomorrow.

Software does have fundamental problems arising from fundamental differences
with tangible objects.  But the transition from the Age of Manufacturing to
the Age of Information provides a nearly infinite incentive for us to solve
them. I'm desperately afraid that we won't, that we'll stick with the old
paradigm that brought us the software crisis, and let our competition find 
the solution unopposed.

There *is* a silver bullet, and I believe that our competitors in the far
east are using it already. Even though they're far behind in software
technology today, they're *already* doing one to two orders of magnitude
than we are in software defect rates (some companies report 0.008 defects
per KSLOC, vs 1-3 defects/KSLOC typical, .01-.1 for critical software,
according to McGarry and Basili figures). Simplifying greatly, the
difference seems to be that they're focusing on the *product* whereas 
we're focusing on the *process*; which is one way of phrasing the paradigm
shift I'm referring to.

>The software crisis has been with us for at least 25 years and will probably
>be with us for at least another 25. Whether C++ or Objective C or something
>else provides and/or will provide the greatest amount of relief from our
>current problems and leverage for our new projects is for the users to decide.

What we, as software's present producers, believe is totally incidental.
It is the *consumers* of our products who's opinion governs the outcome.
It will be totally immaterial who developed them or whether they're coded
in assembler, Cobol, Ada, C++, or Objective-C.

I do admit to more than passing interest that we who view ourselves
as 'the software development community' not discover that our consumers
have done to us what they did to an automobile industry that proved
more attuned to its own interests, rather than its consumers'.
-- 

Brad Cox; cox@stepstone.com; CI$ 71230,647; 203 426 1875
The Stepstone Corporation; 75 Glen Road; Sandy Hook CT 06482

bobatk@microsoft.UUCP (Bob ATKINSON) (07/07/90)

In article <11011@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
>In article <DEUTSCH.90Jul2150016@atlantic.parcplace.com>, deutsch@parcplace.com (Peter Deutsch) writes:
>
>> Closures can be implemented with absolutely no overhead for places that
>> don't use them, and relatively low overhead for places that DO use them.
>
>I won't repeat your description of the technique here, but I will say
>that it looks like a nice one.  It does have one disadvantage,
>and that is that there are some machines that do not allow the
>program counter to point into data space.  Also, it is presumably
>necessary to create these little pieces of code for those stack
>frames that need them.




See also Thomas M. Breuel, "Lexical Closures for C++", 1988 USENIX C++
Conference procedings for a reasonably detailed discussion of exactly
the implementation technique that Peter describes.

In the paper he points out that modifications of the technique will work
uncooperative machines that prohibit the program counter from pointing
into data space.  I quote:



    "We can use another trick to implement lexical closures even on
these architectures.  We pre-allocate in instruction space an array of
instruction sequences of the form:

	stub_n	move location_n,R1	[ed: fetch static link]
		move (R1),SL	 	
		move location_n+4,R1	[ed: fetch real routine address]
		jmp  (R1)	

    We use this array as a stack to allocate and deallocate closure stubs.
A corresponding array of locations in data space holds the actual pointers 
to the code and the static link chain of the closures.  These two new stacks
behave essentially like the runtime stack.  In particular, longjmp must
be modified to restore the two stack pointers for the stub stack and the
location stack appropriately."


This of course statically limits the dynamic depth of the stack of 
closure activations.


	Bob Atkinson
	Microsoft

bobatk@microsoft.UUCP (Bob ATKINSON) (07/07/90)

>Peter Deutsch writes:
>> [paraphrased by rga] "C today lets us take pointers to local variables,
>> even though they are `dangerous'.  One can imagine a similar form of 
>> `dangerous' closures".


Then lawrence.g.mayka writes:
>A closure which remains valid *even after the return of its
>enclosing function* is qualitatively different from the current C
>and C++ treatment of pointers to local variables.

I believe this mis-interprets what Peter said.


>>However, I can imagine using a scheme (:-)) like this for Lisp.
>
>Common Lisp supports closures with indefinite lifetimes, and which
>are therefore garbage-collected.

One can imagine an implemenation of Lisp in which the actual allocation
of a garbage collected closure object does not occur at the time the 
closure is created, but rather when the activation record of its outer
scope returns.  In the iterim, the closure could live on the stack, as
Peter described.  If it doesn't outlive its outer scope, no garbage
collected object need occur.  

Of course, all this depends on the internals of your Lisp.


	Bob Atkinson
	Microsoft

bobatk@microsoft.UUCP (Bob ATKINSON) (07/07/90)

>Peter Deutsch writes:
>> [paraphrased by rga] "C today lets us take pointers to local variables,
>> even though they are `dangerous'.  One can imagine a similar form of 
>> `dangerous' closures".

It seems to me that these form of closures would suffice for a 
significant fraction of the situations in which I find myself
using closures in Smalltalk, upwards of 85% or so.  Most of these
are involved in enumeration of one form or another, and do *not*
require the closure to outlive the activation record of its outer 
scope.


I would find a `dangerous' closure construct extremely useful.
But, you say, we already have a mechanism in C++ that supports
these situations: Enumerator classes.  To wit:


	// The generic enumerator class, from which
	// all enumerators will derive.
	//
	class ENUMERATOR
	{
		int	(*pfnNext)():	// Call to get next element
		void	(*pfnEnd)();	// Call at premature loop termination
	public:
		int  Next()	{ return pfnNext(); }
		void End();	{ pfnEnd(); }
	};

			 

	// An enumerator for enumerating in forward order
	// the elements of a COLL (collection) class.
	//
	class ENM_COLL: public ENUMERATOR 
	{
	// Insert necessary private enumeration state here.
	public: 	
		ELE *pelement; 
		void Init(COLL *pcoll);	
			// Implemenation not shown but it will set
			// pfnNext and pfnEnd.
	};



    // ......


	COLL *pcoll;		
	ENM_COLL enmColl;

	enmColl.Init(pcoll);
	while(enmColl.Next())
	    {
	    // ...
	    // Do something with enmColl.pelement as desired
	    // ...
	    if (someCondition)
		{
		enmColl.End();
		break;
		}
	    }


(Person's more proficient with C++ most certainly could improve on the
coding style presented here.  Asking the COLL itself for an enumerator
is one example.)



Some things are of note:

1) The enumerator class essentially construct a stack frame by hand: if
we had `dangerous' closures, what is found in the enumerator would instead be
kept in the activation record of COLL::Do(/*sometype*/ pfn).  This has
always seemed a little strange to me: why should I have to build by
hand that which the system build _for_ me most of the time?


2) Each different manner of enumerating some aspect of an object 
requires a different enumerator class, since a different "stack frame"
will be required.  Complex objects have several properties which one
might want to enumerate.  For example, a word processing document
might provide methods for enumerating the sections, paragraphs, words,
tables, pictures, headers, footers, misspelled words, etc., that it
contains.  A Hypercard card might provide enumerations of its buttons,
its fields, or the available fonts.  A Dictionary needs to enumerate 
both its keys and values.

In the absence of programming environment support, a proponderence (sp?) 
of auxilliary classes burdens the programmer with the task of documenting
which classes are important, and which are relatively minor.  A natural
way to attempt to understand a unfamiliar piece of code is first to ask 
"what classes are there?".  If it they all seem of equal importance, then
their sheer number may be overwhelming.  The important classes must be 
documented to stand out from the minor ones.

Lack of support for such disctinctions in Smalltalk-80 I believe is one of the 
causes of the general feeling that creating a new class is a "big event"
in that environment.


3) The call to ENUMERATOR::End() is always needed at premature loop
termination, in case the object being enumerated is maintaining
some special state (such as "pending deletions", for instance).
In Smalltalk-80 V2.5, I would write:


	do: aClosure
		[self noteEnumerationBeginning.
		1 to: self size: do: [:index |
			aClosure value: (self at: index)].
		] valueNowOrOnUnwindDo:
			[self noteEnumerationEnding].

and in a client:

	coll do: [:each |
	    "..."
    	    "Do something with each as desired"
	    "..."
	    someCondition
		ifTrue: [^self]].


Notice that the client need not worry that the _implemenation_ of 
do: requires special handling on loop termination.  When closures
are used, abormal loop termination corresponds _precisely_ to an
early return from the "Do" procedure (via a non-local return in
this Smalltalk example, and probably most often via a goto or a 
break in C++).  "valueNowOrOnUnwindDo:" provides a hook that
gets called at early (or non-early) return.  Such a mechanism,
together with closures, would permit us to write C++ code that 
truly hid from clients the implemenation details of enumeration.

I strongly such suspect that such a "return hook" could also be
used by the system to implement the "walk the stack, call the
destructor" part of an exception handling mechanism.  Food for
thought...



As always, comments and thoughts are most welcome.

	Bob Atkinson
	Microsoft

plogan@mentor.com (Patrick Logan) (07/07/90)

(Warning: My comments are based on using Smalltalk-80 and Smalltalk/V
occaissionally for a few months in 1987. I think I recall the latest
ParcPlace version has blocks that are more like "first class"
procedures.)

I find Smalltalk's blocks to be unnecessarily limited in usefulness.
Compare them to Scheme's "first class" procedures which can be passed
freely "upwards" and "downwards". It would be truly awkward, IMO, to
give C/C++ some form of Smalltalk's blocks without generalizing it to
include simple nested functions, for example.

-- 
Patrick Logan  uunet!mntgfx!plogan        |
Mentor Graphics Corp. 8500 SW Creekside P |
Beaverton, Oregon 97005-7191              |

ark@alice.UUCP (Andrew Koenig) (07/08/90)

In article <15625@vlsisj.VLSI.COM>, davidc@vlsisj.VLSI.COM (David Chapman) writes:

> And I find I design hardware the same way I write software.  A NAND gate is
> in some sense the Turing machine of logic designers.  After all, our software
> runs on hardware.  Why shouldn't they be similar at the most fundamental
> levels?

Because the abstractions we use to build them are fundamentally different.

For example, software is much easier to change than hardware, so people
assume it can be changed.  If the processor you're using is missing your
favorite instruction, you live with it, but if your compiler doesn't handle
your favorite pet syntax, you start bugging the author to change it.

This difference in mid set pervades all aspects of software development.
-- 
				--Andrew Koenig
				  ark@europa.att.com

lgm@cbnewsc.att.com (lawrence.g.mayka) (07/09/90)

In article <11036@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
>For example, software is much easier to change than hardware, so people
>assume it can be changed.  If the processor you're using is missing your
>favorite instruction, you live with it, but if your compiler doesn't handle
>your favorite pet syntax, you start bugging the author to change it.
>
>This difference in mid set pervades all aspects of software development.

And so eventually, software system designers will have to decide:
Do we stonewall customer needs forever, or do we start building easily
modifiable systems - systems designed for incremental evolution over
extended periods of time?


	Lawrence G. Mayka
	AT&T Bell Laboratories
	lgm@iexist.att.com

Standard disclaimer.

lgm@cbnewsc.att.com (lawrence.g.mayka) (07/09/90)

In article <5339@stpstn.UUCP> cox@stpstn.UUCP (Brad Cox) writes:
>The silver bullet is not a technology, but a paradigm shift. It is a
>*cultural* change, not a technological one, in precisely the sense that
>Copernicus, Kepler and others forged a silver bullet for the astronomy
>crisis. It was not some supercomputer or programming language for computing
>epicycles more rapidly. It was a shift in that culture's viewpoint in 
>which the astronomers realized that they (and the earth) were not the 
>center of the universe, but a cog in a larger wheel circling round the sun.

A more topical example would be the Polish economy, which is currently
undergoing a massive paradigm shift: from central planning to a free
market.  Events in Poland illustrate a number of points relevant to this
discussion:

1) The paradigm shift was opposed by all those who stood to lose the
special status and privileges they had under the previous system.

2) Proponents of the paradigm shift attempted to press their case
several times, over a period of decades, before its final acceptance.
Each time, "experts" declared the paradigm shift "dead."

3) Defenders of the status quo had, in a sense, theory on their side.
They pointed out that if all needs were known in advance, and
if all available supplies were known in advance, and if all producer
and consumer activity could be effectively controlled centrally,
and if this enormous set of simultaneous equations could be solved
exactly, then the economy would run more efficiently than a market-driven
economy possibly could.  That the economy was not working well
in practice was conveniently ignored, or sometimes covered up
through misrepresentation of evidence.

4) The paradigm shift finally came when it became obvious to those
in power that neither the status quo nor minor "tuning" would suffice
to turn the economy around.

5) The paradigm shift is indeed causing some dislocations, all the
more because it was postponed for so long.  The populace is putting
up with this suffering because of their deep conviction that the
previous system was and is unacceptable.

6) Those who pioneer the paradigm shift - the "early adopters" -
take a significant risk, but can look forward to the largest payoff
if they manage themselves well.  And again, attempting to hold on
to the previous system may represent a greater risk in the long run.

>according to McGarry and Basili figures). Simplifying greatly, the
>difference seems to be that they're focusing on the *product* whereas 
>we're focusing on the *process*; which is one way of phrasing the paradigm
>shift I'm referring to.

This reminds me of an article written by Richard N. Foster which appeared
in the 5/24/82 issue of Business Week.  Titled "A Call for Vision in
Managing Technology," it includes a list of ten major signs that
a company is approaching the limits of its current technology:

	+ An intuitive sense among top managers that the company's
	  R&D productivity is declining.

	+ A trend toward missed R&D deadlines.

	+ A trend toward process rather than product improvement.

	+ A perceived loss of R&D creativity.

	+ Disharmony among the R&D staff.

	+ Lack of improvement from replacement of R&D leaders or staff.

	+ Profits that come from increasingly narrower market segments.

	+ Loss of market share - particularly to a smaller competitor -
	  in a specialized market niche.

	+ Little difference in returns despite spending substantially
	  more - or less - than competitors over a period of years.

	+ Smaller competitors that are taking radical approaches that
	  "probably won't work."

Are any of these signs appearing in the software systems industry?


	Lawrence G. Mayka
	AT&T Bell Laboratories
	lgm@iexist.att.com

Standard disclaimer.

kim@spock (Kim Letkeman) (07/09/90)

In article <5339@stpstn.UUCP>, cox@stpstn.UUCP (Brad Cox) writes:

| There *is* a silver bullet, and I believe that our competitors in
| the far east are using it already. Even though they're far behind in
| software technology today, they're *already* doing one to two orders
| of magnitude than we are in software defect rates (some companies
| report 0.008 defects per KSLOC, vs 1-3 defects/KSLOC typical, .01-.1
| for critical software, according to McGarry and Basili figures).
| Simplifying greatly, the difference seems to be that they're
| focusing on the *product* whereas we're focusing on the *process*;
| which is one way of phrasing the paradigm shift I'm referring to.

I'm partial to the data put forth by DeMarco and Lister that there is
a significant (order of magnitude+) difference between the output of
the best software designers versus the worst in terms of time required
to completion of a project and defect rate (and, one has to assume
long term maintainability.)  They carry it further and show that there
is approximately a 3x difference for those above the median versus
those below.

This would allow for another theory as to why our competitors in the
far east are doing very well in this area.  The information that I am
regularly fed by news services leads me to believe that they have a
rather competitive and selective society. Perhaps they simply have an
overall higher quality population of software design staff (more
talent, better training, whatever.) They may also not have our
designer ego problem - meaning that they may adhere more to
established practices and standards (using software IC equivalents?)
rather than forging a new direction every time the opportunity is
presented.

| I do admit to more than passing interest that we who view ourselves
| as 'the software development community' not discover that our
| consumers have done to us what they did to an automobile industry
| that proved more attuned to its own interests, rather than its
| consumers'.

A frightening thought.
-- 
Kim Letkeman    mitel!spock!kim@uunet.uu.net

diamond@tkou02.enet.dec.com (diamond@tkovoa) (07/11/90)

In newsgroup comp.object, in article <5339@stpstn.UUCP>, cox@stpstn.UUCP (Brad Cox) writes:

|| There *is* a silver bullet, and I believe that our competitors in
|| the far east are using it already. Even though they're far behind in
|| software technology today, they're *already* doing one to two orders
|| of magnitude than we are in software defect rates (some companies
|| report 0.008 defects per KSLOC, vs 1-3 defects/KSLOC typical, .01-.1
|| for critical software, according to McGarry and Basili figures).
|| Simplifying greatly, the difference seems to be that they're
|| focusing on the *product* whereas we're focusing on the *process*;
|| which is one way of phrasing the paradigm shift I'm referring to.

In article <3835@kim> kim@spock (Kim Letkeman) writes:

>I'm partial to the data put forth by DeMarco and Lister that there is
>a significant (order of magnitude+) difference between the output of
>the best software designers versus the worst in terms of time required
>to completion of a project and defect rate (and, one has to assume
>long term maintainability.)  They carry it further and show that there
>is approximately a 3x difference for those above the median versus
>those below.

More likely, an order of magnitude between the best and the AVERAGE.
(I do about five times average when permitted, and I've known a few
who do better.)  Between average and worst is surely several more orders
of magnitude.

>This would allow for another theory as to why our competitors in the
>far east are doing very well in this area.  The information that I am
>regularly fed by news services leads me to believe that they have a
>rather competitive and selective society. Perhaps they simply have an
>overall higher quality population of software design staff (more
>talent, better training, whatever.) They may also not have our
>designer ego problem - meaning that they may adhere more to
>established practices and standards (using software IC equivalents?)
>rather than forging a new direction every time the opportunity is
>presented.

If told to obey established practices and standards, they do so.
The existence or quality of practices and standards still varies from
company to company.

Perhaps the biggest factor is that the usual stereotype of Japanese
society is turned on its head.  If a nail sticks out, Japanese society
hammers it down, while American society does not.  However, in a
high-tech company, if an engineer sticks out, American (Americanized)
managers hammer it down, while Japanese (style) managers might not.
They don't necessarily act on suggestions, but they listen.

|| I do admit to more than passing interest that we who view ourselves
|| as 'the software development community' not discover that our
|| consumers have done to us what they did to an automobile industry
|| that proved more attuned to its own interests, rather than its
|| consumers'.

>A frightening thought.

Just today, in soc.culture.japan, there were a set of articles quoting
an American in Japan, a successful distributor of software.  He feels these
same concerns considerably more strongly and urgently.  Readers interested i
 this topic might look for those articles.

Follow-ups to this article have been directed to comp.misc,soc.culture.japan
since they've drifted from comp.object.
-- 
Norman Diamond, Nihon DEC     diamond@tkou02.enet.dec.com
This is me speaking.  If you want to hear the company speak, you need DECtalk.

oz@yunexus.yorku.ca (Ozan Yigit) (07/11/90)

In article <55700@microsoft.UUCP> bobatk@microsoft.UUCP (Bob ATKINSON) writes:

>One can imagine an implemenation of Lisp in which the actual allocation
>of a garbage collected closure object does not occur at the time the 
>closure is created, but rather when the activation record of its outer
>scope returns. ...

A good overview of just about every scheme (:-) involving continuations
and control is an article by Clinger at al:

	%A William D. Clinger
	%A Anne H. Hartheimer
	%A Eric M. Ost
	%T Implementation Strategies for Continuations
	%J Conference Record of the 1988 ACM Conference on Lisp
	and Functional Programming
	%P 124 131
	%D August 1988
	%K contimpl
	
There has been a lot of work in this topic in the last decade, and I can
post few more refs if needed.

sjs@roland.ctt.bellcore.com (Stan Switzer) (07/12/90)

In article <55703@microsoft.UUCP> bobatk@microsoft.UUCP (Bob Atkinson) writes:
> >Peter Deutsch writes:
> >> [paraphrased by rga] "C today lets us take pointers to local variables,
> >> even though they are `dangerous'.  One can imagine a similar form of 
> >> `dangerous' closures".
> 
> It seems to me that these form of closures would suffice for a 
> significant fraction of the situations in which I find myself
> using closures in Smalltalk, upwards of 85% or so.

Indeed.  Even a dangerous closure would be useful.  Since a dangerous
closure corresponds to an automatic variable, it does raise the
questionof whether there should be a form of closure which corresponds
to C's heap memory, though.

Then again, wantin' and gettin is two different things, and I'm not
going to loose sleep thinking about useful extensions to the C
language.

> But, you say, we already have a mechanism in C++ that supports
> these situations: Enumerator classes.

An excellent observation.  I hadn't thought of it as an enumerator,
but of course!

In fact, I did something like this once in Objective-C.  Basically, I
built a coroutine-like environment where individual "tasks" were coded
as classes and each step was a method.  Invocation of a (sub-)task was
in two steps: instantiating the task (with its parameters, etc) and
starting it.  Instantiation was via the new: method.  The "start"
method specified which task to continue and at which method (a poor
man's continuation).  If you deconstruct this admittedly tedious
scenario you'll see that "task" object instance records correspond
exactly to a stack of activation records, even to the point of
containing a previous stack pointer (task to resume), return location
(method to invoke), and local variables (instance variables).
Fortunately, most of this apparatus could be hidden in a common
superclass and subtask invocation could be made reasonably nice
looking using a few macros.

Bill goes on to describe a few of the difficulties:

> This has
> always seemed a little strange to me: why should I have to build by
> hand that which the system build _for_ me most of the time?

Takes a while for these ideas to sink in, I guess.

> 2) Each different manner of enumerating some aspect of an object 
> requires a different enumerator class, since a different "stack frame"
> will be required.

Yeah, verily.  Having nested classes might go a long way in solving
this problem and quite a few others.

Another problem is that loops don't look like loops since the looping
is generally done by setting up the "next" method as a previously
executed method.  Oldtimers will recognise this as a go-to.  Oldtimers
will also remember that it is possible to do structured programming
with gotos, but if at all possible it is better to have the compiler
write your gotos for you.  Along these lines, one could write a
scripting prepreocessor which transforms reasonable looking control
structures into chains of method invocations.  I think some company in
the Netherlands sells something kinda like this.

Anyway, it seems about time that activation records and instance
records were completely integrated notions and that closure and
object instantiation were similarly unified.  Food for thought.

> As always, comments and thoughts are most welcome.

Likewise,

Stan Switzer  sjs@bellcore.com