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