davidm@uunet.UU.NET (David S. Masterson) (02/11/90)
I'm curious as to the reasons that one might use inheritance (derivation of a new object from the definition of an old object) and the reasons that one might use composition (wrapping multiple objects in another object). I have been getting definitions around my work area that tend to conflict with my own views and I'd like to build a body of evidence one way or the other in order to help make my own views understood. Anyone care to provide their definitions with examples as to how to apply their definitions? -- =================================================================== David Masterson Consilium, Inc. uunet!cimshop!davidm Mt. View, CA 94043 =================================================================== "If someone thinks they know what I said, then I didn't say it!"
ark@alice.UUCP (Andrew Koenig) (02/12/90)
In article <CIMSHOP!DAVIDM.90Feb11005056@uunet.UU.NET>, cimshop!davidm@uunet.UU.NET (David S. Masterson) writes: > I'm curious as to the reasons that one might use inheritance (derivation of a > new object from the definition of an old object) and the reasons that one > might use composition (wrapping multiple objects in another object). The key difference in C++ is that if class D inherits [publicly] from class B, then you can pass a D argument to any function that takes a B parameter. Another way of looking at it is that if you derive D from B, then a D object does [at least] everything a B object does. If you define a class D with a B member, then you must define every operation you want a D to be able to perform, even if that is replicating many of the B operations. -- --Andrew Koenig ark@europa.att.com
drich@klaatu.lanl.gov (David Rich) (02/12/90)
In article <CIMSHOP!DAVIDM.90Feb11005056@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes: I'm curious as to the reasons that one might use inheritance (derivation of a new object from the definition of an old object) and the reasons that one might use composition (wrapping multiple objects in another object). David Masterson uunet!cimshop!davidm When I make use of "inheritance" as you've defined it, I'm typically creating an "is-a" relationship between the old and new object. In other words, I want the new object to behave like the old object plus some additional functionality, etc. When I think of "composition", I think of two kinds: (1) composition via multiple inheritance and (2) composition via encapsulation, or ownership (for lack of a better term). When I make use of (1), I'm simply applying the "is-a" relationship multiple times. When I do this, I know that I want the composite object to be able to have direct access to all the attributes and methods of its ancestors. However, when the composite object shouldn't really have direct access to the attributes and methods of its ancestors, each ancestor becomes an attribute of the composite object (in the form of an object reference). In other words, the composite object needs the functionality, but the relationship is not an "is-a" type. Here's an example from a simulation project that I'm working on. I've built up a map object that represents an interface to terrain data such as relief, vegetation, mobility, roads, rivers, etc. The map encapsulates this information and provides methods that will allow objects to query the state of the terrain, generate routes to move along, etc. The question that came up was how to give this functionality to objects that need to move around on terrain. Using multiple inheritance to give a truck, for example, the ability (among other things) to construct movement routes over terrain doesn't seem right to me. The inheritance mechanism gives the truck access to information that it really shouldn't have (e.g., the truck can change the state of terrain directly by modifying attributes). The alternative, it seems to me, is to give the truck a map attribute. That is, a reference to a map object through which it can deal with terrain appropriately (via the maps "public" interface and not "under the table" so to speack). I guess the point I'm trying to make here is that composition via multiple inheritance may give the composite object information it doesn't need or shouldn't have access to (if there isn't a natural "is-a" type relationship). Composition by encapsulation (which may not be an appropriate term since I just made it up) is a way around this. This "inheritance"/"composition" issue is one we've been trying to get a grasp on too. It seems that the line (for us anyway) is somewhat subjective. What's "is-a" to one person may not be to another. In a lot of ways, this may just be another "soap box" issue. Dave -- -- David Rich Military Systems Analysis Group (A-5) MS F602 Los Alamos National Laboratory Los Alamos, NM 87545 Phone: (505) 665-0726 Email: dor@lanl.gov
davidm@uunet.UU.NET (David S. Masterson) (02/13/90)
In article <10465@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: In article <CIMSHOP!DAVIDM.90Feb11005056@uunet.UU.NET>, [I] wrote: > I'm curious as to the reasons that one might use inheritance (derivation > of a new object from the definition of an old object) and the reasons > that one might use composition (wrapping multiple objects in another > object). > The key difference in C++ is that if class D inherits [publicly] from class B, then you can pass a D argument to any function that takes a B parameter. Another way of looking at it is that if you derive D from B, then a D object does [at least] everything a B object does. If you define a class D with a B member, then you must define every operation you want a D to be able to perform, even if that is replicating many of the B operations. This is interesting... The tone here suggests that one idea is good and the other is not so good (intentional?). Also, except for "derive...[when] object does everything [the base] object does", there's no real suggestion of a model to follow here. Its more of rules of thumb about programming style. This is one of the points that I seem to be finding in the use of C++ (and perhaps generally in OOP) by programmers with a light background in the concepts (I'm certainly NOT suggesting that Andrew Koenig has a light background in the concepts). Instead of a design model to follow, the approach seems to be more of "do what seems right programmatically" and the design will take care of itself. Is this appropriate or do people see the need for more "principles" to follow to promote properly designed, reusable code development? -- =================================================================== David Masterson Consilium, Inc. uunet!cimshop!davidm Mt. View, CA 94043 =================================================================== "If someone thinks they know what I said, then I didn't say it!"
ark@alice.UUCP (Andrew Koenig) (02/13/90)
In article <CIMSHOP!DAVIDM.90Feb12100359@uunet.UU.NET>, cimshop!davidm@uunet.UU.NET (David S. Masterson) writes: > In article <10465@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: > The key difference in C++ is that if class D inherits [publicly] from > class B, then you can pass a D argument to any function that takes > a B parameter. Another way of looking at it is that if you derive > D from B, then a D object does [at least] everything a B object does. > This is interesting... > The tone here suggests that one idea is good and the other is not so good > (intentional?). Also, except for "derive...[when] object does everything [the > base] object does", there's no real suggestion of a model to follow here. I do not intend to imply that one alternative is good and the other bad -- I'm just trying to point out the differences. Each approach has its place. It's a little like trying to decide whether to use screws or nails -- each can sometimes substitute for the other, but each has advantages and disadvantages. Which one you choose depends on the details of the problem you're trying to solve. -- --Andrew Koenig ark@europa.att.com
dag@control.lth.se (Dag Bruck) (02/13/90)
In article <CIMSHOP!DAVIDM.90Feb12100359@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes: >In article <10465@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: > The key difference in C++ is that if class D inherits [publicly] from > class B, then you can pass a D argument to any function that takes > a B parameter. > >Instead of a design model to follow, the approach seems to be more of "do what >seems right programmatically" and the design will take care of itself. Is >this appropriate or do people see the need for more "principles" to follow to >promote properly designed, reusable code development? In general, I think insight is more important than principles. We often talk about reuse, but we must distinguish between (at least) two different forms of reuse: reuse of code and reuse of abstraction. The "American" argument for OOP is to reuse existing code, while the "European" argument is to use inheritance as a structuring and abstraction mechanism. I find both equally important, but reuse of code is much easier than reuse of abstraction. I also believe reuse of code naturally lends itself to bottom-up development, while reuse of abstraction lends itself to top-down development. In practice we often alternate between the top-down and the bottom-up approaches. The C++ world is almost singularly concerned with code reuse. This is demonstrated whenever C++ is described, and also when Andrew Koenig uses a function call for describing inheritance. It is also obvious that many Smalltalk, Objective-C and Eiffel friends believe that reuse of abstraction is next to impossible in C++ -- a complete misconception in my view. Dag M. Bruck
gza@mentor.cc.purdue.edu (William R Burdick) (02/13/90)
The difference between inheritance (or generalization) and composition (or aggregation) is a tough point to figure out without some good examples (I hope I can give some here). Depending on what language you use and what you use your objects for, you may not be able to tell what the difference is right off. Mainly, inheritance passes on behavior to subclasses and composition passes on form to aggregations. As an easy way to tell the difference right off (using multiple inheritance), consider readable files, writable files, and read-write files. You can say that a read-write file inherits behavior from readable files and writable files. If you were to try to make a read-write file out of a composition, you might end up with an object containing two files, a readable file and a writable file. If you make a Read-Write File (the class) a subclass of Read File and Write File, you get a kind of file which can be read from and written to. classes use inheritance and classes are definitions of objects. Instances use composition, and instances are the objects. A particular read-write file does not inherit behavior from its superclass because it is not a class, it is a file, while a read-write file could be composed of a read file and a write file. When you want to model objects which are made of other objects, use composition, when you want to model objects which behave like other kinds of objects, use inheritance. The key words here are 'made of,' 'other objects,' 'behave like,' and 'kinds of objects.' When I use 'kinds of objects,' I am referring to objects of other classes, when I use 'other objects,' I am referring to just any other objects. An object composed of other objects shares a 'made-of/part-of' relationship with those objects, while a class shares a 'has-a/is-a' relationship with its subclasses. In OOPLs, you can use instance variables for the part-of relationship and you can use subclassing for the is-a relationship. -- -- Bill Burdick burdick@cello.ecn.purdue.edu
pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi) (02/13/90)
In article <10465@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: In article <CIMSHOP!DAVIDM.90Feb11005056@uunet.UU.NET>, cimshop!davidm@uunet.UU.NET (David S. Masterson) writes: > I'm curious as to the reasons that one might use inheritance (derivation of a > new object from the definition of an old object) and the reasons that one > might use composition (wrapping multiple objects in another object). The key difference in C++ is that if class D inherits [publicly] from class B, then you can pass a D argument to any function that takes a B parameter. Another way of looking at it is that if you derive D from B, then a D object does [at least] everything a B object does. If you define a class D with a B member, then you must define every operation you want a D to be able to perform, even if that is replicating many of the B operations. But this is just syntactic sugaring. And it leads to a lot of problems as well, like all the funny rules about multiple inheritance, and the ambiguity between inheritance as is-a and as part-of, and so on. The inheritance issue is bogus and clouds the real problem, that in sufficiently sophisticated applications you have a data modeling and design problem of the same size and complexity as that of a data base, and you need to apply the same conceptual tools, and map the concepts onto ultimately contiguity or pointers (the only ways we have in a computer to indicate a relationship between data). Just think that inheritance could be entirely obviated if one were allowed to use composition with nameless members, or if a member could be identified by any unambiguous path to it, like in PL/1. Example: class vehicle { ... int weight, speed, cost; ... } v0; class car : vehicle { ... int seats; ... } c0; class truck : vehicle { ... int load; ... } t0; vs. (assuming you have unnamed members): class vehicle { ... int weight, speed, cost; ... } v1; class car { ... vehicle; int seats; ... } c1; class truck { ... vehicle; int load; ... } t1; or (assuming memberscan be named by any unambiguous way): class vehicle { ... int weight, speed, cost; ... } v2; class car { ... vehicle v; int seats; ... } c2; class truck { ... vehicle v; int load; ... } t2; You can write 'c0.speed' or 'c1.weight' or 't2.cost' indifferently; the same goes of course for function members (which should be abolished as well, by the way, by allowing any function whose first argument is of a class type to be invoked in infix position). Getting rid of inheritance, and using only composition with some sugaring to make members of members visible at the outer level, is entirely possible. You also need a few paraphernalia, like sensible casts between (pointer to a member)/(to the enclosing structure) and viceversa, and sensible member pointers. It is desirable on two grounds: that inheritance is redundant, and it clouds serious discussions about the many problems of data modeling using composition. I strongly believe that inheritance was invented *only* because in Simula 67 class objects could only be accessed via references, and 'prefixing' (as it was more correctly called) was needed as an efficient way of concantenating object representations. The essentially linear nature of prefixing is what causes all the conceptual and implementation problems with MI. Notice also that in some cases MI has actually to be implemented using references in C++. Doesn't this point to a fundamental problem with the concept? :-(. -- Piercarlo "Peter" Grandi | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcvax!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk
jimad@microsoft.UUCP (JAMES ADCOCK) (02/14/90)
In article <10465@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: >The key difference in C++ is that if class D inherits [publicly] from >class B, then you can pass a D argument to any function that takes >a B parameter. Another way of looking at it is that if you derive >D from B, then a D object does [at least] everything a B object does. >If you define a class D with a B member, then you must define every >operation you want a D to be able to perform, even if that is replicating >many of the B operations. More particularly, if you want to be able to use D as a B--and then some, one must either/or inherit from B and make non-virtual methods of B available unchanged from D, and/or inherit from B and possibly change some of the virtual methods of B to reflect the added functionality of D. In either case, the public methods of B can be thought of as a protocol that D must conform to. The important thing to note is that in C++, unlike some other OOPLs, even if one delared the same-named methods as B in a class D' *not* derived from B, those same-named methods *would-not* match B's protocol. So D' could not be used as a B, even though D' *seemed* to have all the correctly-named methods necessary to act like a B. D can be used as a B only via inheritence.
marc@dumbcat.UUCP (Marco S Hyman) (02/14/90)
In article <CIMSHOP!DAVIDM.90Feb12100359@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes:
Instead of a design model to follow, the approach seems to be more of
"do what seems right programmatically" and the design will take care
of itself. Is this appropriate or do people see the need for more
"principles" to follow to promote properly designed, reusable code
development?
You start out trying to follow the right principles but end up satisfying
the customer. That is, when the ultimate goal is to ``finish the damn thing
and make it work'' you tend to do ``what seems right programmatically.''
How good or bad this is in the long run depends upon the language being used
and the skill level of the programmer. The more highly skilled the
programmer, the more s/he can do with the language and the more that
``seems right.'' If the language supports the principles you wish to follow
the easier it is to ``do it right.''
Or am I the only one that feels frustrated when real world constraints force
me to program in C or Assembler after I've been having so much fun with
Smalltalk or C++?
// marc {ames,pyramid,sun}!pacbell!dumbcat!marc
djones@megatest.UUCP (Dave Jones) (02/15/90)
From article <PCG.90Feb13153607@rupert.cs.aber.ac.uk>, by pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi): ... > > The inheritance issue is bogus and clouds the real problem ... Hear! Hear! I've been wanting to say this, but I've been reluctant to get into the scrap with people who know all the fashionalble buzz-words. > ... [ to ] map the concepts ultimately onto contiguity or > pointers (the only ways we have in a computer to indicate a > relationship between data). > That's what this all comes down to isn't it? That and what kind of shorthand-resolution you want the compiler to do for you. (Not easy questions, actually.) When I get into a discussion of this stuff, and inevitably somebody says something like, 'But that can all be solved with post-recurrent semideclined prevaricated polytrophism,' I say, 'Mumble, ... refresh my memory on what that means. What do the compiler's lookup-tables do, and when do you use pointers at runtime?' > Just think that inheritance could be entirely obviated if one were > allowed to use composition with nameless members, or if a member could > be identified by any unambiguous path to it, like in PL/1. > Did PL/1 do that? It's been so long since I used that language -- over fifteen years I guess -- I've forgotten most everything about it. PL/1 has been ragged so hard over the years, it seems strange to hear one of its features mentioned in a positive way. For the last few years, just for the fun of it, I've been dreaming up my own sequential imperative language. Don't know if I'll ever get around to implementing it. I had decided that all this 'inheritance' and 'composition' debate comes down to nothing more than when to use pointers and what kind of shorthand you prefer. I decided on a scheme similar to the ones you describe. Ditto with function-members. It's good to know that at least one other person on this planet sees things the way I do.
adiseker@potomac.ads.com (Andrew Diseker) (02/15/90)
In article <CIMSHOP!DAVIDM.90Feb11005056@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes: >I'm curious as to the reasons that one might use inheritance (derivation of a >new object from the definition of an old object) and the reasons that one >might use composition (wrapping multiple objects in another object). I have >been getting definitions around my work area that tend to conflict with my own >views and I'd like to build a body of evidence one way or the other in order >to help make my own views understood. Anyone care to provide their >definitions with examples as to how to apply their definitions? > >-- >=================================================================== >David Masterson Consilium, Inc. >uunet!cimshop!davidm Mt. View, CA 94043 >=================================================================== >"If someone thinks they know what I said, then I didn't say it!" What follows are my opinions only, flames will be gleefully ignored (I've been roasted by the best %^). From my experience working with and having discussions with some excellent OO programmers, I think the answer to the first question about derivation could be this: If there is an existing definition of an object that is close ( however you define that ) to what you want to use, then use your language's inheritance mechanism to create a copy of the definition and modify the parts that don't do quite what you want, and add whatever you need for any extra functionality. There, I didn't use the dreaded 'c' word, didn't mention data hiding, or any other religious terms. I tried to keep it general enough that it could even apply to (gag, choke) ADA. Now, however, it's time to don the asbestos suit. The second part of your question, referring to composition classes, is a matter of strong opinions on two sides. My personal reason for using composition objects is to circumvent the need for multiple inheritance. If I have two or more classes which exhibit the functionallity I need, and which contain the data in which I'm interested, then one object which contains instances of those classes and which allows whatever access is needed to those instances, is all I need. I just haven't seen any schemes for MH ( acronyms, anyone? ) that are general enough to be effective, and specific enough to cover all the holes. If you want to know what I'm talking about, just watch the next hundred or so responses to this post! %^) %^) %^) I really hate to have my compiler ( environment, whatever ) bogged down trying to prevent or otherwise handle the abiguities that naturally arise when you try to munge multiple, possibly disparate, possibly related classes. I call it a "what happens when cousins marry?" problem. Ahem. Now that I've offended any number of people, I'll step aside and watch the mayhem. Hope I've helped in some way, though. -- Andrew Diseker ( )-( ) Mouse-Tested! Advanced Decision Systems /o o\ Hacker-Approved! UUCP: sun!sundc!potomac!adiseker / =v= \ ,--_ Internet: adiseker@potomac.ads.com ;;---;; `--'
sjs@spectral.ctt.bellcore.com (Stan Switzer) (02/16/90)
Dave Jones (djones@megatest.UUCP) writes: > From article <PCG.90Feb13153607@rupert.cs.aber.ac.uk>, > by pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi): > > > ... [ to ] map the concepts ultimately onto contiguity or > > pointers (the only ways we have in a computer to indicate a > > relationship between data). > > That's what this all comes down to isn't it? That and what kind of > shorthand-resolution you want the compiler to do for you. (Not easy > questions, actually.) I was meaning to comment on this earlier, but I let it pass. There are many ways to represent relationships in a computer. For instance, relational databases (conceptually) use an associative technique based on the equality of attribute values. Another associative technique is pattern matching (grep, and X resource databases). Other types of reference relationships might include inter-addressing-environment references, file references, and persistent-object store references. Even granting the importance of contiguous values and pointer references, there are all sorts of possible pointer semantics: "Atom" semantics (eq <=> equal -- SETL, or some dialect of SETL, did this), capability references, self-describing references, context-described references, and simple storage-indexing memory references. > When I get into a discussion of this stuff, and inevitably somebody > says something like, 'But that can all be solved with post-recurrent > semideclined prevaricated polytrophism,' I say, 'Mumble, ... refresh > my memory on what that means. What do the compiler's lookup-tables do, > and when do you use pointers at runtime?' Sure, there is a lot of terminology out there, some of it needlessly complicated, but this terminology _does_ attempt to make useful distinctions. > For the last few years, just for the fun of it, I've been dreaming up my > own sequential imperative language. What! Another one? Stan Switzer sjs@bellcore.com
jss@jra.ardent.com (02/16/90)
In article <8361@potomac.ads.com> adiseker@potomac.ads.com (Andrew Diseker) writes: > > From my experience working with and having discussions with some >excellent OO programmers, I think the answer to the first question about >derivation could be this: If there is an existing definition of an object >that is close ( however you define that ) to what you want to use, then >use your language's inheritance mechanism to create a copy of the definition >and modify the parts that don't do quite what you want, and add whatever >you need for any extra functionality. This may be good advice for "generic OO" but it is bad advice for C++. I believe that the "protected interface of a class" (i.e. the relation between a class and classes derived from it) must be specified just as carefully as the specification of any member, and if the original class was not designed with such an interface then you shouldn't be deriving from it. Many C++ classes are designed with a value semantics rather than an object semantics. That is, they are intended to have their values copied rather than pointers to them passed around. The classsical example is "complex". Even when it isn't obvious from the external interface, the implementation of a class may copy values around in various ways. It is almost always a mistake to derive from these classes. When I to use a class supplied by somebody else, I use it in the way it was designed to be used. When I want to copy code, I copy it. Jerry Schwarz
sakkinen@tukki.jyu.fi (Markku Sakkinen) (02/16/90)
In article <PCG.90Feb13153607@rupert.cs.aber.ac.uk> pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi) writes: >In article <10465@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: >> ... >But this is just syntactic sugaring. And it leads to a lot of problems >as well, like all the funny rules about multiple inheritance, and the >ambiguity between inheritance as is-a and as part-of, and so on. There is sense behind this oversimplification. However, you could go all the way and say that any programming language is just a universal Turing machine with some syntactic sugaring; it tastes better to me with at least a little sugar. >The inheritance issue is bogus and clouds the real problem, that >in sufficiently sophisticated applications you have a data >modeling and design problem of the same size and complexity as >that of a data base, and you need to apply the same conceptual >tools, and map the concepts onto ultimately contiguity or >pointers (the only ways we have in a computer to indicate a >relationship between data). That we have a limited choice in physical implementation has no bearing on what conceptual tools and models we can or should use. (Besides, there are other ways, e.g. "foreign keys".) Many people in the data base community are applying OO concepts, including inheritance. It is one of the mandatory features suggested in "The object-oriented database system manifesto", which was written by Atkinson, Bancilhon, DeWitt, Dittrich, Maier, and Zdonik for the DOOD'89 conference. >Just think that inheritance could be entirely obviated if one were >allowed to use composition with nameless members, or if a member could >be identified by any unambiguous path to it, like in PL/1. > ... >Getting rid of inheritance, and using only composition with some >sugaring to make members of members visible at the outer level, is >entirely possible. This is pretty much in line with what both Raj and Levy - "A compositional model for software reuse" and myself - "Disciplined inheritance" presented at ECOOP'89. I think both parties had at least played with the idea of calling the paper - you guessed it - "Inheritance considered harmful". > ... > I strongly believe that inheritance was invented *only* because > in Simula 67 class objects could only be accessed via references, > and 'prefixing' (as it was more correctly called) was needed as > an efficient way of concantenating object representations. The > essentially linear nature of prefixing is what causes all the > conceptual and implementation problems with MI. I agree with much of this diagnosis, although I doubt '*only*'. Incidentally, even two very new languages, Modula-3 and Oberon (the latter extremely Spartan even in other ways), seem to follow the same prefixing principle and stick confidently to single inheritance. By the way, it still holds for almost all common OOPL's that class objects can only be accessed via references. - All conceptual problems of MI don't stem from the linear nature of prefixing as far as I can see: e.g. the Flavors model looks bad to me because it doesn't preserve the integrity of the superclass parts. > Notice also that in some cases MI has actually to be implemented > using references in C++. Doesn't this point to a fundamental > problem with the concept? :-(. No, in my analysis (see the ECOOP paper) the MI _principles_ of C++ are sounder than any others I have studied. The _implementation_ of MI has evidently become cumbersome because C++ has offered the strong concept of object identity (as seen in e.g. Smalltalk) on the altar of "efficiency". Markku Sakkinen Department of Computer Science University of Jyvaskyla (a's with umlauts) Seminaarinkatu 15 SF-40100 Jyvaskyla (umlauts again) Finland SAKKINEN@FINJYU.bitnet (alternative network address)
djones@megatest.UUCP (Dave Jones) (02/17/90)
From article <20020@bellcore.bellcore.com>, by sjs@spectral.ctt.bellcore.com (Stan Switzer): ... > >> For the last few years, just for the fun of it, I've been dreaming up my >> own sequential imperative language. > > What! Another one? > Why not? I said, 'just for the fun of it.' Besides, I'm not satisfied with any that I've seen so far.
djones@megatest.UUCP (Dave Jones) (02/17/90)
From article <20020@bellcore.bellcore.com>, by sjs@spectral.ctt.bellcore.com (Stan Switzer): > Dave Jones (djones@megatest.UUCP) writes: >> From article <PCG.90Feb13153607@rupert.cs.aber.ac.uk>, >> by pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi): >> >> > ... [ to ] map the concepts ultimately onto contiguity or >> > pointers (the only ways we have in a computer to indicate a >> > relationship between data). >> >> That's what this all comes down to isn't it? That and what kind of >> shorthand-resolution you want the compiler to do for you. (Not easy >> questions, actually.) > > I was meaning to comment on this earlier, but I let it pass. There > are many ways to represent relationships in a computer. For instance, > relational databases (conceptually) use an associative technique based > on the equality of attribute values. [...] These other relationship techniques you named are all search-and-test operations. You still need some way to find that something to test. Notice that Mr. Switzer used the word 'ultimately'. These search-and-test operations are indeed ways to indicate relationships, but they *ultimately* depend on contiguity or pointers. Contiguity and pointers are the atomic operations of association in digital computers. Still the question remains as to whether you want the higher level operations to be an integral part of a programing language, as hash-tables are, for example, in AWK, or do you want to furnish these capabilities as {utility/library/class} functions which extend a basic language? My current opinion is that if the language has good enough extention mechanisms, it's best to start with only a small set of primitive operations, and build. That way, you don't tend to get 'languaged into a corner' with this language having this feature and that language having that one, and no way to mix them. (I often wish I could link my C-procedures into an AWK program.) Problem is, I don't know of any language whose extention mechanisms are 'good enough'. But then, I really haven't been keeping up over the last few years. C++ is close, though.
pcg@aber-cs.UUCP (Piercarlo Grandi) (02/19/90)
In article <20020@bellcore.bellcore.com> sjs@bellcore.com Switzer) writes: Dave Jones (djones@megatest.UUCP) writes: > From article <PCG.90Feb13153607@rupert.cs.aber.ac.uk>, > by pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi): > > > ... [ to ] map the concepts ultimately onto contiguity or > > pointers (the only ways we have in a computer to indicate a > > relationship between data). > > That's what this all comes down to isn't it? That and what kind of > shorthand-resolution you want the compiler to do for you. (Not easy > questions, actually.) I was meaning to comment on this earlier, but I let it pass. There are many ways to represent relationships in a computer. I beg to differ. In a *computer* you have memory as a block of storage units, and composite entities can only be built out of contiguity or pointers. C++ is a language that tries hard to let you do *implementation* level work, that is "full unrestricted" access to the underlying hardware. The type constructors in C++, and other machine oriented HLLs, are records and arrays for heterogenous and homogeneous contiguity, and pointers. For instance, relational databases (conceptually) use an associative technique based on the equality of attribute values. Another associative technique is pattern matching (grep, and X resource databases). Other types of reference relationships might include inter-addressing-environment references, file references, and persistent-object store references. But these are all *program* level abstractions. One of the big tasks of programming is to map all the useful data structuring abstractions you have (and these are usually content based) onto contiguity and pointers. An especially hard job because contiguity is bad for dynamic resizing and pointers by their very nature are one way only, and this has many profound consequences (e.g. on garbage collection) in all the common cases where you have many-to-many abstractions to represent (just think that essentially network databases are there only to give you bidirectional pointers). It is notable about C++ is that BS has carefully designed it to retain by and large (the two notable exceptions being constructors/destructors and multiple virtual inheritance) the MOHLL flavour of C. Eiffel for example, not to speak of CLOS or Simula 67, do not have this aim. What I find disagreeable about virtual multiple inheritance (I also don't like much constructors destructors, incidentally) is that what you think is an object is actually implemented as a graph (tree) of subobjects. This may well be too high level. It is my impression that thsi is because of prefixing. -- Piercarlo "Peter" Grandi | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcvax!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk
pcg@aber-cs.UUCP (Piercarlo Grandi) (02/19/90)
In article <12029@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes: > allowed to use composition with nameless members, or if a member could > be identified by any unambiguous path to it, like in PL/1. Did PL/1 do that? It's been so long since I used that language -- over fifteen years I guess -- I've forgotten most everything about it. PL/1 has been ragged so hard over the years, it seems strange to hear one of its features mentioned in a positive way. I seem to remember that also Cobol allows you to name a member with the shortest unique path to it. This feature may be seen as dangerous, as it means that modifying a data structure can make a program ambiguous, but this is really unavoidable, with inheritance as well. I had written in my C++ coding guidelines to prefix all identifiers used in a function with their scope operators. Somebody observed that this also should extend to members (prefixing them from the name of the class from which they are inherited), obviously not just to improve readability (where is this member from?), but also to guard against hazardous ambiguities caused by extending member lists in the future. As to PL/1, it actually had some *good* ideas, and some ridiculous ones (exceptions as a datatype!, 22/7 resulting in overflow,...). For example areas, based pointers, controlled storage. Not that I think they were all appropriate at the language level; for example, controlled storage is done in GNU LIB C++ as the obstack class, and areas can be done in C++ 2.0 with operator new overloading. On the other hand I would really be happy to see based (relative) pointer support, which is vital to get position independent data structures (and no, "smart pointers" are not a full solution). -- Piercarlo "Peter" Grandi | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcvax!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk