* Re: Choice of OO primitives in Ada95 [not found] ` <4g2f8v$15lc@watnews1.watson.ibm.com> @ 1996-02-19 0:00 ` Don Harrison 1996-02-19 0:00 ` Norman H. Cohen ` (2 more replies) 1996-02-21 0:00 ` John DiCamillo 1 sibling, 3 replies; 27+ messages in thread From: Don Harrison @ 1996-02-19 0:00 UTC (permalink / raw) Norman H. Cohen) wrote: :|In article <DMu9yw.5ts@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) :|writes: : :|> Tucker Taft wrote: :|> :|> [...] :|> :|> : ... The orthogonality of :|> : types and modules in Ada 95 can be a major advantage, :|> : gives the programmer more flexibility in structuring their system :|> :|> What flexibility does this approach offer over conventionally encapsulated classes? : :1. It allows other forms of encapsulation and packaging, not involving : classes. We could have a package providing a nonextendible private : type for complex numbers, deferred constants zero, one, and i, and a : set of arithmetic operations. We could have a package providing a set : of constants. We could have a package providing a set of : higher-level auxiliary routines operating on a type provided by : another package, building on the primitive operations provided by that : package. I've seen people try to use C++ classes with no nonstatic : members for these other forms of encapsulation and packaging, and the : result is strained. That's one reason C++ has added namespaces, which : are like Ada packages. But once the full generality of Ada-like : packages is available, the class program unit as an encapsulation : mechanism is redundant. I don't question the value of packages for non-OO encapsulation. I am interested to know what is the perceived flexibility wrt OO. Did Tucker meant flexibility wrt non-OO encapsulation? :2. It allows data abstractions involving two or more intimately linked : types to be encapsulated together. For example: : : with Dates; use Dates; : package Family_Trees is : : type Marriage_Type is private; : type Person_Type is private; : : function New_Person : (Name : String; : Birth_Date : Date_Type; : Death_Date : Date_Type := No_Date); : : procedure Wed : (Husband : in out Person_Type; : Wife : in out Person_Type; : Marriage_Date : in Date_Type; : Divorce_Date : in Date_Type := No_Date; : Marriage : out Marriage_Type); : : procedure Add_Offspring : (Marriage: in out Marriage_Type; Child: in out Person_Type); : : function Husband (Marriage: in Marriage_Type) return Person_Type; : function Wife (Marriage: in Marriage_Type) return Person_Type; : function Marriage_Of (Spouse: in Person_Type) return Marriage_Type; : function Parents (Child: in Person_Type) return Marriage_Type; : : generic : with procedure Process_One_Child (Child: in Person_Type); : procedure Process_All_Children (Marriage: in Marriage_Type); : : generic : with procedure Process_One_Marriage (Marriage: in Marriage_Type); : procedure Process_All_Marriages (Spouse: in Person_Type); : : private : : ... : : end Family_Trees; This is an example of what I mean by tight-coupling of abstractions - you are forced to encapsulate both abstractions into a single module. :|> As far as symmetry between operands is concerned, the benefits seem to be more :|> theoretical than practical. : :The ability to use natural notation has the very practical advantage of :making programs easier to read and understand. I would say the opposite is true - forcing a whole bunch of different abstractions into the one module creates an amorphous mess that the developer has to sift through to see which operations relate to which abstractions. :|> Indeed, encapsulating classes has the practical :|> advantage of cleaner interfaces to classes. Cleaner interfaces facilitate easier :|> debugging, maintenance, reuse etc. The Ada 95 model works against this principle :|> by forcing designers to create tightly-coupled classes because parts of their :|> interfaces are shared. : :Ada does not "force" designers to do any such thing. Indeed, the :language encourages the use of encapsulated, loosely coupled data :abstractions. An example might best illustrate what I mean. Extending your example and assuming each type is tagged, we might have: package X is type MARRIAGE is tagged ... type PERSON is tagged ... type UNIVERSITY is tagged ... type GOVERNMENT is tagged ... type ELECTORATE is tagged ... type ADDRESS is tagged ... procedure Enrol (Student : in out PERSON; Uni : in out UNIVERSITY); procedure Award_Grant (Polies : in out GOVERNMENT; Uni : in out UNIVERSITY); procedure Make_Gerrymander (Polies : in out GOVERNMENT; Elect : in out ELECTORATE); function Office_Address (Elect : in ELECTORATE) return ADDRESS; end X; Each abstraction is related to the previous one but isn't necessarily related to any other. As I understand it, the language rules dictate that each of these abstractions must be in the same package. But many of them have nothing to do with each other. They are tightly-coupled even though they should be distinct and this has been forced by the language rules. :|> Forcing ownership of operations by particular classes is entirely consistent with :|> the pattern established in Ada 83 for packages: the components of a package OWN :|> their components (attributes, operations, etc.). Therfore, mapping classes onto :|> packages giving package classes follows quite naturally as shown by my model. : :Ada certainly recognizes a close relationship among packages, types, and :operations: If a package provides both a type and subprograms with :parameters or results of that type, those subprograms "belong" to the :type. (In the technical jargon, they are "primitive operations" of the :type.) : :But a package is a module, not a type. The unnatural modeling of :one-per-class data items as static "members" in C++ comes from an attempt :to force-fit the two concepts into a single construct. In Ada, a :singleton data item is simply a variable declared inside a :package--encapsulated in the package body if it is to be hidden (like a :C++ private static data member). There is no pretense that the variable :is in any way a COMPONENT of any type that happens to be encapsulated in :the same package as the variable. I can't make any comment on C++ as I'm unfamiliar with it (must be the only one in the world). The Eiffel equivalent of static members are probably 'once' attributes which may optionally be exported (visible or private). This issue is language war flame bait and I don't intend to defend the pure OO position here :-) : :-- :Norman H. Cohen ncohen@watson.ibm.com Don. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-19 0:00 ` Choice of OO primitives in Ada95 Don Harrison @ 1996-02-19 0:00 ` Norman H. Cohen 1996-02-19 0:00 ` Robert A Duff 1996-02-21 0:00 ` Robert I. Eachus 2 siblings, 0 replies; 27+ messages in thread From: Norman H. Cohen @ 1996-02-19 0:00 UTC (permalink / raw) In article <Dn0FqH.8Cw@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: |> I don't question the value of packages for non-OO encapsulation. I am interested |> to know what is the perceived flexibility wrt OO. Did Tucker meant flexibility |> wrt non-OO encapsulation? I can't speak for Tucker, but my argument is as follows: Packages, private parts, and package bodies are an appropriate mechanism for non-OO encapsulation that can be used in precisely the same way for OO encapsulation, equally appropriately. Therefore, a second, quite different mechanism for OO encapsulation would be redundant. |> :2. It allows data abstractions involving two or more intimately linked |> : types to be encapsulated together. For example: [my family-tree example deleted] |> |> This is an example of what I mean by tight-coupling of abstractions - you are |> forced to encapsulate both abstractions into a single module. No, I wasn't forced to do so, I chose to do so, because Marriage_Type and Person_Type are part of a single recursive data structure and the family-tree data abstraction has many operations that are most efficiently implemented by referring to both the Marriage_Type and Person_Type parts of this data structure. Other applications entail mutually dependent types that are not "initimately linked" in this way, and in such cases separate packages are more appropriate. A programmer whose encapsulation mechanism is a package has the freedom to choose whether or not the two types should be encapsulated together. A programmer whose encapsulation mechanism is the class does not. |> :|> As far as symmetry between operands is concerned, the benefits seem to be more |> :|> theoretical than practical. |> : |> :The ability to use natural notation has the very practical advantage of |> :making programs easier to read and understand. |> |> I would say the opposite is true - forcing a whole bunch of different abstractions |> into the one module creates an amorphous mess that the developer has to sift |> through to see which operations relate to which abstractions. This is a non sequitur. Your original remark about symmetry being of only theoretical interest referred to the ability to define truly symmetric binary dispatching operations. I replied that this ability provides a practical rather than theoretical advantage in terms of program understandability. You reply with a false and in any event irrelevant suggestion that packages "force" different abstractions into one module. |> An example might best illustrate what I mean. Extending your example and assuming |> each type is tagged, we might have: |> |> package X is |> type MARRIAGE is tagged ... |> type PERSON is tagged ... |> type UNIVERSITY is tagged ... |> type GOVERNMENT is tagged ... |> type ELECTORATE is tagged ... |> type ADDRESS is tagged ... |> |> procedure Enrol (Student : in out PERSON; Uni : in out UNIVERSITY); |> procedure Award_Grant (Polies : in out GOVERNMENT; Uni : in out UNIVERSITY); |> procedure Make_Gerrymander (Polies : in out GOVERNMENT; Elect : in out ELECTORATE); |> function Office_Address (Elect : in ELECTORATE) return ADDRESS; |> |> end X; |> |> Each abstraction is related to the previous one but isn't necessarily related |> to any other. As I understand it, the language rules dictate that each of these |> abstractions must be in the same package. You understand it incorrectly! |> But many of them have nothing to do with |> each other. They are tightly-coupled even though they should be distinct and this |> has been forced by the language rules. There are several solutions for writing data abstractions that have mutually dependent interfaces, but independent implementations, in separate packages. John Volan and I have debated two such approaches in this newsgroup ad nauseum. Ada packages give you the CHOICE of packaging two types together or separately. In the family-tree example, the two types are different aspects of the same abstraction, with a shared implementation, and a single package is appropriate. (Other examples: A package providing a type for linked lists and a type for cursors representing positions in linked lists; a package providing a type for points on a time line and a type for lengths of intervals on a time line; a package providing a type for machine addresses and a type for offsets between machine addresses; a package providing a type for sparse matrices, a type identifying a row of such a sparse matrix, and a type identifying a column of a sparse matrix; etc.) When it is not appropriate to package two mutually dependent types together, Ada packages give you the CHOICE to encapsulate them separately. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-19 0:00 ` Choice of OO primitives in Ada95 Don Harrison 1996-02-19 0:00 ` Norman H. Cohen @ 1996-02-19 0:00 ` Robert A Duff 1996-02-20 0:00 ` Don Harrison 1996-02-21 0:00 ` Robert I. Eachus 2 siblings, 1 reply; 27+ messages in thread From: Robert A Duff @ 1996-02-19 0:00 UTC (permalink / raw) In article <Dn0FqH.8Cw@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >package X is > type MARRIAGE is tagged ... > type PERSON is tagged ... > type UNIVERSITY is tagged ... > type GOVERNMENT is tagged ... > type ELECTORATE is tagged ... > type ADDRESS is tagged ... > > procedure Enrol (Student : in out PERSON; Uni : in out UNIVERSITY); > procedure Award_Grant (Polies : in out GOVERNMENT; Uni : in out UNIVERSITY); > procedure Make_Gerrymander (Polies : in out GOVERNMENT; Elect : in out ELECTORATE); > function Office_Address (Elect : in ELECTORATE) return ADDRESS; > >end X; > >Each abstraction is related to the previous one but isn't necessarily related >to any other. As I understand it, the language rules dictate that each of these >abstractions must be in the same package. No, they don't have to be in the same package (and as you say, probably should not be). Could you explain what you mean -- why do you think that all of the above types have to be in the same package? (By the way, the above code is illegal, by 3.9.2(12).) >I can't make any comment on C++ as I'm unfamiliar with it (must be the only >one in the world). The Eiffel equivalent of static members are probably 'once' >attributes which may optionally be exported (visible or private). No, static members in C++ are not quite the same thing as Eiffel's 'once' attributes. A static member is just a function that is inside the class for visibility/encapsulation purposes, but isn't attached to any particular object. In other words, there's no "self" or "this" when you call a static member. A once attribute is a function that gets executed just once, and the result gets saved -- when you call it again, you get the saved value. In Ada, you would just use a constant for that purpose, in most cases. >... This issue >is language war flame bait and I don't intend to defend the pure OO position >here :-) Once attributes are flame bait? Not sure why anybody would get too hot about that issue. OK, if you say so. - Bob ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-19 0:00 ` Robert A Duff @ 1996-02-20 0:00 ` Don Harrison 1996-02-20 0:00 ` Jon S Anthony ` (2 more replies) 0 siblings, 3 replies; 27+ messages in thread From: Don Harrison @ 1996-02-20 0:00 UTC (permalink / raw) Robert A Duff writes: :In article <Dn0FqH.8Cw@assip.csasyd.oz>, :Don Harrison <donh@syd.csa.com.au> wrote: :>package X is :> type MARRIAGE is tagged ... :> type PERSON is tagged ... :> type UNIVERSITY is tagged ... :> type GOVERNMENT is tagged ... :> type ELECTORATE is tagged ... :> type ADDRESS is tagged ... :> :> procedure Enrol (Student : in out PERSON; Uni : in out UNIVERSITY); :> procedure Award_Grant (Polies : in out GOVERNMENT; Uni : in out UNIVERSITY); :> procedure Make_Gerrymander (Polies : in out GOVERNMENT; Elect : in out ELECTORATE); :> function Office_Address (Elect : in ELECTORATE) return ADDRESS; :> :>end X; :> :>Each abstraction is related to the previous one but isn't necessarily related :>to any other. As I understand it, the language rules dictate that each of these :>abstractions must be in the same package. : :No, they don't have to be in the same package (and as you say, probably :should not be). Could you explain what you mean -- why do you think :that all of the above types have to be in the same package? I don't have an RM available but quoting you (25.1.96) on the subject of dispatching operations in response to Arcadio A. Sincero: > >As a matter of fact, the only indication that TPerson's Walk "belongs to > >it" is that TPerson's Walk has a TPerson parameter. > That, plus the fact that it's in the same package. So, if you want all of the operations to be dispatching (primitive), then the tagged types must also be in the same package. In the same thread, Jon S Anthony went on to say that non-dispatching operations using tagged types were those defined in different packages to those types: > Just to add one more bit (completely beating it to death...), it is also > legal to have a subprogram with operands of both types as long as it is > not in the package where the two types are declared. Of course such a > subprogram is not primitive and will never dispatch (assuming it has no > other controlling operands). Dispatching or not dispatching depending on where an operation is defined is not what you would call consistent. A couple of other gripes: 1) Why should you have to specify that a type is 'tagged'? Can't the compiler work that out for itself? eg. by seeing whether the type is extended elsewhere. The developer is forced to worry about what should be an implementation issue. 2) Similarly, why should the developer have to specify that an operation dispatches (classwide operations)? Presumably, you're aiming for quicker execution, but compilers could perform a certain degree of optimisation eg. If it knows the type is not extended anywhere, there is no need to dispatch. There would also be situations where the specific variant of an inherited type is known eg. following an explicit assignment from an entity of that type. [...] :- Bob Don. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` Don Harrison @ 1996-02-20 0:00 ` Jon S Anthony 1996-02-22 0:00 ` Real OO (was Choice of OO primitives in Ada95) Don Harrison 1996-02-20 0:00 ` Choice of OO primitives in Ada95 Ray Toal 1996-02-23 0:00 ` Robert A Duff 2 siblings, 1 reply; 27+ messages in thread From: Jon S Anthony @ 1996-02-20 0:00 UTC (permalink / raw) In article <Dn22M0.G0D@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Robert A Duff writes: > > :In article <Dn0FqH.8Cw@assip.csasyd.oz>, > :Don Harrison <donh@syd.csa.com.au> wrote: > :>package X is > :> type MARRIAGE is tagged ... > :> type PERSON is tagged ... > :> type UNIVERSITY is tagged ... > :> type GOVERNMENT is tagged ... > :> type ELECTORATE is tagged ... > :> type ADDRESS is tagged ... > :> > :>... > :>end X; > :> > :>Each abstraction is related to the previous one but isn't necessarily > :>related to any other. As I understand it, the language rules dictate > :>that each of these abstractions must be in the same package. > : > :No, they don't have to be in the same package (and as you say, probably > :should not be). Could you explain what you mean -- why do you think > :that all of the above types have to be in the same package? > > I don't have an RM available but quoting you (25.1.96) on the > subject of dispatching operations in response to Arcadio A. Sincero: > > > >As a matter of fact, the only indication that TPerson's Walk "belongs to > > >it" is that TPerson's Walk has a TPerson parameter. > > > That, plus the fact that it's in the same package. > > So, if you want all of the operations to be dispatching (primitive), then the > tagged types must also be in the same package. What in this in anyway could even _possibly_ be construed in the course of even the most tortuous path of illogic to be saying _all_ the types _together_ need to be in the _same_ package? Hmmmmmm???? > In the same thread, Jon S Anthony went on to say that > non-dispatching operations using tagged types were those defined in > different packages to those types: > > > Just to add one more bit (completely beating it to death...), it is also > > legal to have a subprogram with operands of both types as long as it is > > not in the package where the two types are declared. Of course such a > > subprogram is not primitive and will never dispatch (assuming it has no > > other controlling operands). See above (possibly even more so). > Dispatching or not dispatching depending on where an operation is > defined is not what you would call consistent. Why not? Seems pretty simple really. And it allows for non dispatching operations to be constructed if they have a closer match to the desired semantics. And why should operations that are closely tied to the semantics of a type (basically the intent of primitive operations) be definable anywhere? That sort of thing leads pretty quickly to inscrutable structures. (I believe that Dylan does allow this sort of thing, but there has been disagreement on how "wonderful" it is.) > A couple of other gripes: > 1) Why should you have to specify that a type is 'tagged'? Can't > the compiler work that out for itself? eg. by seeing whether the > type is extended elsewhere. The developer is forced to worry about > what should be an implementation issue. Ahhh, the ol' clairvoyant compiler. You are on a roll. What if the type is not extended? This sort of comment might make sense if you were discussing a language where the only kinds of types available were "classes" (actually, it would not even make sense there as then it would be irrelevant). But you aren't. Of course you can decry this fact, but that is another matter altogether and irrelevant given the current context. Also, given the context, it is very much the case that "tagged" is _not_ simply an implementation issue. Flagging a particular type as one which is extensible and supportive of dynamic polymorphism can be argued to be a "good engineering" tradeoff. > 2) Similarly, why should the developer have to specify that an > operation dispatches (classwide operations)? Presumably, you're > aiming for quicker execution, but compilers could perform a certain > degree of optimisation eg. If it knows the type is not extended > anywhere, there is no need to dispatch. There would also be > situations where the specific variant of an inherited type is known > eg. following an explicit assignment from an entity of that type. This is just plain wrong - on several accounts. First class wide operations do not dispatch (they have uses more akin to a "generic method" in CLOS, though clearly more restricted). Second, _all_ primitive operations are dispatching operations. The "developer" does _not_ specify that an operation dispatches (other than simply including it as a primitive operation of a tagged type). Third, optimization is irrelevant _to the semantics_ of whether any actual _invocation_ dispatches. Sans optimizations a primitive operation dispatches iff it has class-wide actuals for the operation's controlling parameters - the compiler _always_ knows when a particular call of an operation dispatches. Fourth, optimization is also (mostly) irrelevant for making the design choice of keeping instances of dynamic polymorphism a local rather than global issue. /Jon -- Jon Anthony Organon Motives, Inc. 1 Williston Road, Suite 4 Belmont, MA 02178 617.484.3383 jsa@organon.com ^ permalink raw reply [flat|nested] 27+ messages in thread
* Real OO (was Choice of OO primitives in Ada95) 1996-02-20 0:00 ` Jon S Anthony @ 1996-02-22 0:00 ` Don Harrison 1996-02-22 0:00 ` Jon S Anthony ` (4 more replies) 0 siblings, 5 replies; 27+ messages in thread From: Don Harrison @ 1996-02-22 0:00 UTC (permalink / raw) To Norman Cohen: Thanks for giving the example about Family Trees. I've gained access to an RM and Rationale. I'll go and check whether my understanding is correct. (I was asking for trouble debating an issue without the reference material handy). To Jon Anthony: On the necessity or otherwise of "tagged" and "'Class", I acknowledge that with hybrid OO model adopted in Ada 95, it may be appropriate to require these of developers - I'll reserve judgement until I've re-read the RM and Rationale. --------------------------------------------------------------------------------- The real issue to me is that the Ada 95 OO model falls short of what real object- orientation is about. The purpose of OO, as I see it is this: "To enable the cost-effective development of correct, reliable and maintainable software". The sub-goals required to facilitate this are: a) Allow decomposition of the software into intelligible modules each of which represents a clearly defined abstraction and has a clearly defined interface with other abstractions b) Allow the construction, where possible, of abstractions using abstractions that are already defined, customising them as necessary rather than defining abstractions from sctrach. c) Allow the valid state of an abstraction to be clearly defined and enforced. In order to acheive those goals optimally, an OO language should offer: 1) encapsulated abstractions (classes) 2) inheritance/polymorphism of those abstractions 3) control over the state of those abstractions (assertions). Taking each in turn (and not attempting to justify them), 1) Best provided by pure OO (eg. Eiffel, Smalltalk) followed by hybrid OO language with proper classes (extendable types mapped onto modules) eg. (C++, myriad others) followed by hybrid OO with non-encapsulated classes (eg. Ada, CLOS?). 2) The linchpin of OO - any language claiming to be OO offers this. 3) The only significant OO language I know of that offers this is Eiffel. Assertions are executable conditions which must be satisfied to guarantee that an abstraction has a valid state whenever it is updated. These include: i) pre-conditions on operations that callers are required to satisfy ii) invariant conditions that must always be true when the abstraction is updated iii) post-conditions on operations stating what the abstraction guarantees to have done when the operation has been executed. There are others. Note that Ada only fully satisfies 2) and has a sub-optimal mechanism for 1). While Eiffel is strong in the OO stakes (it satifies all 3), it is weak (unfortunately) in the RT arena. These weaknesses are: a) poor low level facilities (bit fiddling) b) GC renders it an inappropriate tool for hard real-time systems (although the emergence of mainstream parallel architectures will permit this) c) no concurrency (an elegant model currently being implemented - not yet part of the standard). I think these will all be addressed in time but if none are a concern to you, I suggest taking a look at Eiffel. Don. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-22 0:00 ` Real OO (was Choice of OO primitives in Ada95) Don Harrison @ 1996-02-22 0:00 ` Jon S Anthony 1996-02-22 0:00 ` Robert Dewar ` (3 subsequent siblings) 4 siblings, 0 replies; 27+ messages in thread From: Jon S Anthony @ 1996-02-22 0:00 UTC (permalink / raw) In article <dewar.824966245@schonberg> dewar@cs.nyu.edu (Robert Dewar) writes: > Don says > > "Note that Ada only fully satisfies 2) and has a sub-optimal mechanism for 1)." > > Well that's no more than an opinion, you provide no objective criterion to > justify this opinion. In fact I think that Ada 95 satisfies 1) more > convincingly than Eiffel. This has been discussed many times on this news > group, and I don't particularly have anything to add to those previous > discussions, I think it has all been said before! > > The value of executable assertions has been hotly debated. To regard them > as an essential part of OO programming seems odd to me, but of course > people are free to say "x is essential for OO in my view" for almost > any X (and all sorts of x's have been suggested). One trouble with > executable assertions is that they are at the wrong level of abstraction, > since they are confined to be at the semantic level of the language, and > generally you want to talk about abstractions at a higher level. Yes, Don says a lot of things. He also says the following in c.l.e: <<< Newsgroups: comp.lang.eiffel Path: alexandria.organon.com!uunet!in2.uu.net!usc!howland.reston.ans.net!nntp.coast.net!harbinger.cc.monash.edu.au!news.mel.connect.com.au!news.syd.connect.com.au!warrane.connect.com.au!assip.csasyd!news From: donh@syd.csa.com.au (Don Harrison) Subject: Real OO Message-ID: <Dn5sLw.A95@assip.csasyd.oz> Sender: news@assip.csasyd.oz Reply-To: donh@syd.csa.com.au Organization: CSC Australia Date: Thu, 22 Feb 1996 04:03:31 GMT Lines: 11 For anyone interested, I've just stirred up the Ada nest with a posting titled 'Real OO'. I didn't cross post it to here, for fear of boring you all with endless follow-ups. Regards, Don. >>> Basically, he's just trolling. So, it is best to not waste time replying to anything he posts. /Jon -- Jon Anthony Organon Motives, Inc. 1 Williston Road, Suite 4 Belmont, MA 02178 617.484.3383 jsa@organon.com ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-22 0:00 ` Real OO (was Choice of OO primitives in Ada95) Don Harrison 1996-02-22 0:00 ` Jon S Anthony @ 1996-02-22 0:00 ` Robert Dewar 1996-02-23 0:00 ` Gene Ouye 1996-02-24 0:00 ` Valery Croizier ` (2 subsequent siblings) 4 siblings, 1 reply; 27+ messages in thread From: Robert Dewar @ 1996-02-22 0:00 UTC (permalink / raw) Don says "Note that Ada only fully satisfies 2) and has a sub-optimal mechanism for 1)." Well that's no more than an opinion, you provide no objective criterion to justify this opinion. In fact I think that Ada 95 satisfies 1) more convincingly than Eiffel. This has been discussed many times on this news group, and I don't particularly have anything to add to those previous discussions, I think it has all been said before! The value of executable assertions has been hotly debated. To regard them as an essential part of OO programming seems odd to me, but of course people are free to say "x is essential for OO in my view" for almost any X (and all sorts of x's have been suggested). One trouble with executable assertions is that they are at the wrong level of abstraction, since they are confined to be at the semantic level of the language, and generally you want to talk about abstractions at a higher level. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-22 0:00 ` Robert Dewar @ 1996-02-23 0:00 ` Gene Ouye 1996-02-26 0:00 ` James O'Connor 0 siblings, 1 reply; 27+ messages in thread From: Gene Ouye @ 1996-02-23 0:00 UTC (permalink / raw) You realize, of course, that to the outside world (more specifically, to many that I know in the C++ community), watching this arguing between Eiffel and Ada advocates is like watching two hyenas fighting over the last scraps of a carcass after the pride of lions has had its fill... You also realize, of course, that I'm not trying to say that any of this discussion is futile (if I ever find that to be true, I will be very depressed!). Gene Ouye <geneo@rational.com> ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-23 0:00 ` Gene Ouye @ 1996-02-26 0:00 ` James O'Connor 1996-02-26 0:00 ` Gene Ouye 0 siblings, 1 reply; 27+ messages in thread From: James O'Connor @ 1996-02-26 0:00 UTC (permalink / raw) Gene Ouye wrote: > > You realize, of course, that to the outside world (more > specifically, to many that I know in the C++ community), > watching this arguing between Eiffel and Ada advocates is > like watching two hyenas fighting over the last scraps of > a carcass after the pride of lions has had its fill... > > You also realize, of course, that I'm not trying to say > that any of this discussion is futile (if I ever find that > to be true, I will be very depressed!). > > Gene Ouye <geneo@rational.com> You also realize that to the Smalltalk world, the argument is interesting because, from a Smalltalk perspective, Ada95 and C++ have much more in common than the do differeneces... James O'Connor oconnor@apci.net ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-26 0:00 ` James O'Connor @ 1996-02-26 0:00 ` Gene Ouye 0 siblings, 0 replies; 27+ messages in thread From: Gene Ouye @ 1996-02-26 0:00 UTC (permalink / raw) James O'Connor wrote: > You also realize that to the Smalltalk world, the argument is interesting > because, from a Smalltalk perspective, Ada95 and C++ have much more in > common than the do differeneces... True, but most of the arguing in this thread(s) was over Eiffel vs Ada. I actually found it refreshing after the endless "C/C++ sucks" threads, Unfortunately, I've gained most of my Smalltalk knowledge from the endless "C++ is not (or, Smalltalk is) a pure OO language" threads on comp.object. Maybe we could use more Smalltalk vs. Ada arguments in this group... ;-) Gene Ouye <geneo@rational.com> ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-22 0:00 ` Real OO (was Choice of OO primitives in Ada95) Don Harrison 1996-02-22 0:00 ` Jon S Anthony 1996-02-22 0:00 ` Robert Dewar @ 1996-02-24 0:00 ` Valery Croizier 1996-02-24 0:00 ` Robert A Duff 1996-02-26 0:00 ` So called Real OO (was blah blah blah...) Jon S Anthony 4 siblings, 0 replies; 27+ messages in thread From: Valery Croizier @ 1996-02-24 0:00 UTC (permalink / raw) In article <Dn9BzB.IMA@world.std.com> bobduff@world.std.com (Robert A Duff) writes: > You seemed to object strongly to the idea that one can put two types in > the same package, on the grounds that it decreases encapsulation. On > the contrary, it *increases* encapsulation. When two types are closely > related to each other, by their nature, *forcing* them to be in separate > modules means that *more* operations need to be exported from each > module. > [...] > An example is a List type, plus a List_Cursor type. The List_Cursor > points at a certain place in the list, and has operations for moving > forward and/or backward and so forth. These operations cannot be > written without visibility on implementation details of *both* types. > So if class=module, you have to *export* a lot of those implemenation > details from one of them, so the other one can do what it needs to do. > Putting them both in the same module, however, actually gives you > *better* encapsulation, because fewer (and higher-level) operations get > exported. Yes, you have to export internals to the other class, but encapsulation is not weaker, because you can do a selective export, so that no one (except List_Cursor) can see List's internals. Putting two types in the same module weakens encapsulation, because it exports : o *all* internals o to *each other* types in that module, while Eiffel allowes the programmer to export only useful data to classes that need them. For example, type Person has a field Disease. Only a Doctor can see this field, because only doctors are able to identify diseases. You can't put Person and Doctor in the same package, because : o Doctor would see *all* private data of Person and, that is worse, o Person would see all private data of *Doctor*. Ada's solution works fine with the List_Iterator issue, because List and List_Cursor aren't complex structures. > [...] > So I've heard. How does this new elegant model deal with the issue I > mentioned above, about putting list-cursor information inside the list > itself? If you have multiple tasks, you definitely want to have two > tasks iterating through the same shared list (or whatever data > structure). Has the standard class library been changed to solve this > problem? The issue have little to do with tasking. Even in a sequential program, a List can have several clients. As far as Eiffel Libraries are concerned, I don't know them well, but I've just ckecked ISE's and Tower's Web site, and there is no list in the Library Kernel. Of course, vendors provide their own librairies, and the most prominent ones have traversable lists (at least Sig and Tower have). -- Valery CROIZIER ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-22 0:00 ` Real OO (was Choice of OO primitives in Ada95) Don Harrison ` (2 preceding siblings ...) 1996-02-24 0:00 ` Valery Croizier @ 1996-02-24 0:00 ` Robert A Duff 1996-02-26 0:00 ` Matthew B. Kennel 1996-02-26 0:00 ` Don Harrison 1996-02-26 0:00 ` So called Real OO (was blah blah blah...) Jon S Anthony 4 siblings, 2 replies; 27+ messages in thread From: Robert A Duff @ 1996-02-24 0:00 UTC (permalink / raw) In article <Dn5qGH.9xH@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >... (I was asking for trouble debating an issue without >the reference material handy). :-) :-) :-) >Note that Ada only fully satisfies 2) and has a sub-optimal mechanism for 1). You've said elsewhere that you don't like Ada's support for 1). I suppose it's something of a matter of taste. I happen to think that Ada's support for 1) is *better* than Eiffel, and the other languages that combine Ada's notion of package and type into one. You seemed to object strongly to the idea that one can put two types in the same package, on the grounds that it decreases encapsulation. On the contrary, it *increases* encapsulation. When two types are closely related to each other, by their nature, *forcing* them to be in separate modules means that *more* operations need to be exported from each module. Encapsulation isn't some sort of Goodness, such that the more you have of it, the better. No, you want to encapsulate things at the "right" level of granularity for the problem at hand. Usually, that's a single type, plus the operations belonging to that type. However, in some cases, two types are so closely related that the right granularity is to put them both in the same package. (After all, in Fortran 66, the only module is the subroutine, which is a *smaller* granularity than the class. But that doesn't make the encapsulation better.) An example is a List type, plus a List_Cursor type. The List_Cursor points at a certain place in the list, and has operations for moving forward and/or backward and so forth. These operations cannot be written without visibility on implementation details of *both* types. So if class=module, you have to *export* a lot of those implemenation details from one of them, so the other one can do what it needs to do. Putting them both in the same module, however, actually gives you *better* encapsulation, because fewer (and higher-level) operations get exported. Last time I looked at Eiffel, the standard library classes put the cursor movement stuff inside the list. That seems like a poor design, since you can only be looping through a given list once, at any given time. It seems to me that you badly want the List_Cursor type to be separate from the List type, although they *are* part of the same "module". >While Eiffel is strong in the OO stakes (it satifies all 3), I agree that Eiffel is superior to Ada with respect to your number 3) -- that is, the assertion stuff. Ada does have *some* support for assertions. For example, if I say, "type T is range 1..10;", then there's an invariant on T -- all objects of type T have to be in the range 1 to 10. This is pretty weak, compared to Eiffel, though. I think it's silly to call that part of OO, though. It's a completely orthogonal issue. Assertions would be useful in a non-OO language, too. If you define "OO" as being "anything I happen to think is good", then the term becomes useless, and people end up arguing about how, "My Daddy is more OO than your Daddy." Meyer also said that garbage collection is a fundamental part of OO. Nonsense. GC existed before OO, and it's not necessary in order to support the real fundamentals of OO, such as polymorphism. Why not just say that GC is a Good Thing, or assertions are a Good Thing, or Eiffel's assertions are better than Ada's assertions, or C++ sucks because it makes it hard to implement GC? It's silly to use the term "OO" as a synonym for "the set of all Good Things". >...it is weak >(unfortunately) in the RT arena. These weaknesses are: > >a) poor low level facilities (bit fiddling) >b) GC renders it an inappropriate tool for hard real-time systems (although the > emergence of mainstream parallel architectures will permit this) Some people believe quite strongly that GC is appropriate for hard real-time systems. But that's another thread. >c) no concurrency (an elegant model currently being implemented - not yet part > of the standard). So I've heard. How does this new elegant model deal with the issue I mentioned above, about putting list-cursor information inside the list itself? If you have multiple tasks, you definitely want to have two tasks iterating through the same shared list (or whatever data structure). Has the standard class library been changed to solve this problem? >I think these will all be addressed in time but if none are a concern to you, >I suggest taking a look at Eiffel. Indeed. Even if they *are* a concern, it's still instructive to learn Eiffel, and to read Meyer's classic book on OOP. Programming by contract is a useful concept in any language. - Bob ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-24 0:00 ` Robert A Duff @ 1996-02-26 0:00 ` Matthew B. Kennel 1996-02-26 0:00 ` Don Harrison 1 sibling, 0 replies; 27+ messages in thread From: Matthew B. Kennel @ 1996-02-26 0:00 UTC (permalink / raw) Robert A Duff (bobduff@world.std.com) wrote: : Encapsulation isn't some sort of Goodness, such that the more you have : of it, the better. No, you want to encapsulate things at the "right" : level of granularity for the problem at hand. I definitely agree with this. : An example is a List type, plus a List_Cursor type. The List_Cursor : points at a certain place in the list, and has operations for moving : forward and/or backward and so forth. These operations cannot be : written without visibility on implementation details of *both* types. : So if class=module, you have to *export* a lot of those implemenation : details from one of them, so the other one can do what it needs to do. : Putting them both in the same module, however, actually gives you : *better* encapsulation, because fewer (and higher-level) operations get : exported. Of course, Eiffel has selective export, where individual features or sets of features can be mutually exported and imported. Ada gives this set a name. Better? Perhaps. Eiffel can express notions of "who gets to see what" which are not easily compatible with strictly hierarchical packages. (Consider three classes, A B and C with A sharing some routines with B and some with C, but B and C sharing nothing. ) : Last time I looked at Eiffel, the standard library classes put the : cursor movement stuff inside the list. That seems like a poor design, : since you can only be looping through a given list once, at any given : time. It seems to me that you badly want the List_Cursor type to be : separate from the List type, although they *are* part of the same : "module". I might agree, but there's always the question of efficiency. Most of the time you're looping through one list once, needing to allocate extra objects may be expensive. What I really want, of course, is Sather's iterators which totally solves the problem. :-) : >While Eiffel is strong in the OO stakes (it satifies all 3), : I agree that Eiffel is superior to Ada with respect to your number 3) -- : that is, the assertion stuff. Ada does have *some* support for : assertions. For example, if I say, "type T is range 1..10;", then : there's an invariant on T -- all objects of type T have to be in the : range 1 to 10. This is pretty weak, compared to Eiffel, though. : I think it's silly to call that part of OO, though. It's a completely : orthogonal issue. Assertions would be useful in a non-OO language, too. I wouldn't call it completely orthogonal. A major point of polymorphic object oriented programming, and specificially "design by contract" is validating "who is substitutable for whom here?". Eiffel expresses this notion very directly in the combined type AND assertion system, as descendents must be compatible both in type and assertion set with their ancestors. I'm not a master of Ada, but it seems from reading Ada programs that this information is not quite as readily obvious. : Meyer also said that garbage collection is a fundamental part of OO. : Nonsense. GC existed before OO, and it's not necessary in order to : support the real fundamentals of OO, such as polymorphism. : Why not just say that GC is a Good Thing, or assertions are a Good : Thing, or Eiffel's assertions are better than Ada's assertions, or C++ : sucks because it makes it hard to implement GC? It's silly to use the : term "OO" as a synonym for "the set of all Good Things". No argument. I wait for the day whan "object orientation" is as much an obvious (and uninteresting) prerequisite as "uses Unicode character set in parsing". : >I think these will all be addressed in time but if none are a concern to you, : >I suggest taking a look at Eiffel. : Indeed. Even if they *are* a concern, it's still instructive to learn : Eiffel, and to read Meyer's classic book on OOP. Programming by : contract is a useful concept in any language. : - Bob ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Real OO (was Choice of OO primitives in Ada95) 1996-02-24 0:00 ` Robert A Duff 1996-02-26 0:00 ` Matthew B. Kennel @ 1996-02-26 0:00 ` Don Harrison 1 sibling, 0 replies; 27+ messages in thread From: Don Harrison @ 1996-02-26 0:00 UTC (permalink / raw) Thanks to numerous respondents for your input on this thread. Hope to reply within the next couple of days. No, it is not a troll. Nor is any other I have contributed to. Don. ^ permalink raw reply [flat|nested] 27+ messages in thread
* So called Real OO (was blah blah blah...) 1996-02-22 0:00 ` Real OO (was Choice of OO primitives in Ada95) Don Harrison ` (3 preceding siblings ...) 1996-02-24 0:00 ` Robert A Duff @ 1996-02-26 0:00 ` Jon S Anthony 4 siblings, 0 replies; 27+ messages in thread From: Jon S Anthony @ 1996-02-26 0:00 UTC (permalink / raw) In article <Dn9BzB.IMA@world.std.com> bobduff@world.std.com (Robert A Duff) writes: > You seemed to object strongly to the idea that one can put two types in > the same package, on the grounds that it decreases encapsulation. On > the contrary, it *increases* encapsulation. When two types are closely > related to each other, by their nature, *forcing* them to be in separate > modules means that *more* operations need to be exported from each > module. Yes, but in this context (Eiffel) it is mostly irrelevant. This is because the two classes can export the "detail stuff" to each other _only_. This is from selective export, in particular the optional part of a feature clause (listing its client classes). See ETL 7.12 and 7.13. > mentioned above, about putting list-cursor information inside the list > itself? If you have multiple tasks, you definitely want to have two > tasks iterating through the same shared list (or whatever data > structure). Has the standard class library been changed to solve this > problem? I think you are confused here. Iteration is effected by inheriting from say, list_iteration, when defining some class that needs to be "iterated over". In effect, list_iteration supplies only the pattern, so the particular class has the "cursor" (which is not accessible in any event). To handle more than one iteration, you repeatedly inherit list_iteration (renaming bits as appropriate). Now, the real problem here (as has been discussed to death on c.l.e) is that the repeated inheritance machinery exists in the main to support this iteration aspect and it is rather complicated and subtle machinery to handle such a simple thing. In some respects this bit is the Eiffel counterpart "dirty laudry" to the "mutual withing" problem in Ada: Yes, you can get the thing to work, but it isn't pretty. /Jon -- Jon Anthony Organon Motives, Inc. 1 Williston Road, Suite 4 Belmont, MA 02178 617.484.3383 jsa@organon.com ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` Don Harrison 1996-02-20 0:00 ` Jon S Anthony @ 1996-02-20 0:00 ` Ray Toal 1996-02-21 0:00 ` Don Harrison 1996-02-22 0:00 ` Bernd Holzmueller 1996-02-23 0:00 ` Robert A Duff 2 siblings, 2 replies; 27+ messages in thread From: Ray Toal @ 1996-02-20 0:00 UTC (permalink / raw) donh@syd.csa.com.au (Don Harrison) wrote: >1) Why should you have to specify that a type is 'tagged'? Can't the compiler work >that out for itself? eg. by seeing whether the type is extended elsewhere. The >developer is forced to worry about what should be an implementation issue. Requiring 'tagged' is a GOOD THING! Inheritance weakens encapsulation. The default case (no tagged) is that you design a type, and you provide all and only those operations that work on the type and you don't make any details available to anyone else - in short you fully control the type. Now if your intent is to ALLOW derivation then you must mark it tagged. This alerts the reader that this type may be derived from. Imagine a language in which you could inherit from any type you wanted to! :-) Whether or not a type should be tagged is a DESIGN decision; I totally disagree that it should be an implementation decision. By the way a compiler can not in general determine if the "type is extended elsewhere" since in Ada extensions can appear in other compilation units. >2) Similarly, why should the developer have to specify that an operation dispatches >(classwide operations)? Presumably, you're aiming for quicker execution, but >compilers could perform a certain degree of optimisation eg. If it knows the type >is not extended anywhere, there is no need to dispatch. There would also be >situations where the specific variant of an inherited type is known eg. following >an explicit assignment from an entity of that type. The reason you need to specify whether or not an operation dispatches is that you can write code like procedure P (X: T) is begin R(X); end P; procedure Q (X: T'Class) is begin R(X); end Q; which behave differently. Ray Toal ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` Choice of OO primitives in Ada95 Ray Toal @ 1996-02-21 0:00 ` Don Harrison 1996-02-23 0:00 ` Robert A Duff 1996-02-22 0:00 ` Bernd Holzmueller 1 sibling, 1 reply; 27+ messages in thread From: Don Harrison @ 1996-02-21 0:00 UTC (permalink / raw) Ray Toal wrote: :donh@syd.csa.com.au (Don Harrison) wrote: : :>1) Why should you have to specify that a type is 'tagged'? Can't the compiler work :>that out for itself? eg. by seeing whether the type is extended elsewhere. The :>developer is forced to worry about what should be an implementation issue. : :Requiring 'tagged' is a GOOD THING! Inheritance weakens encapsulation. Not sure what you mean by this. Inheritance is no weaker or stronger in languages (such as Eiffel) which map classes onto the encapsulation mechanism. :The default case (no tagged) is that you design a type, and you :provide all and only those operations that work on the type and you :don't make any details available to anyone else - in short you :fully control the type. Now if your intent is to ALLOW derivation :then you must mark it tagged. This alerts the reader that this :type may be derived from. Imagine a language in which you could :inherit from any type you wanted to! :-) Yes, it's called Eiffel and it's great having such flexibility! :-). One aspect of that flexibility is that, if, one sunny day, you decide you need to extend a type you go right in and do it and don't have to touch the original. In Ada, you have to go back and redefine the type to make it tagged. The impact of this may be limited to that - don't know, haven't thought about it - but you shouldn't have to redefine something to reuse it. : Whether or not a type :should be tagged is a DESIGN decision; I totally disagree that it :should be an implementation decision. By the way a compiler can not :in general determine if the "type is extended elsewhere" since in :Ada extensions can appear in other compilation units. I can't think offhand of a suitable way of dealing with this but that isn't to say it's impossible. :>2) Similarly, why should the developer have to specify that an operation dispatches :>(classwide operations)? Presumably, you're aiming for quicker execution, but :>compilers could perform a certain degree of optimisation eg. If it knows the type :>is not extended anywhere, there is no need to dispatch. There would also be :>situations where the specific variant of an inherited type is known eg. following :>an explicit assignment from an entity of that type. : :The reason you need to specify whether or not an operation dispatches :is that you can write code like : : procedure P (X: T) is begin R(X); end P; : procedure Q (X: T'Class) is begin R(X); end Q; : :which behave differently. You don't have to do it that way. You can use (for example) a synonym construct like procedure P, Q (X: T) is begin R(X); end P, Q; and redefine Q for extensions of X. : :Ray Toal : : Don. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-21 0:00 ` Don Harrison @ 1996-02-23 0:00 ` Robert A Duff 0 siblings, 0 replies; 27+ messages in thread From: Robert A Duff @ 1996-02-23 0:00 UTC (permalink / raw) In article <Dn42C3.21u@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >Ray Toal wrote: >:Requiring 'tagged' is a GOOD THING! Inheritance weakens encapsulation. > >Not sure what you mean by this. Inheritance is no weaker or stronger in languages >(such as Eiffel) which map classes onto the encapsulation mechanism. He didn't say "inheritance" is weaker, he said "encapsulation" is weaker. That's clearly true -- when you see a function call that cannot dispatch, you know for sure exactly what it does. When you see a dispatching call, it might go off and do something you've never heard of. If there's no dispatching, then a single abstraction is completely encapsulated, whereas with dispatching, an overriding function can sneak in and do buggy things. Dispatching is a powerful feature, but it has this cost. If you use dispatching when you don't need it, you are paying that cost without getting any benefit. Therefore, it makes sense to give the programmer the choice. Note that Eiffel seems to agree with this, to some extent -- otherwise, the "frozen" (or whatever it's called) thing wouldn't exist. Of course, the counter-argument is that you don't always know ahead of time when you might need dispatching. I'm not sure I agree with Ray Toal, though, that requiring "tagged" is a Good Thing. Even without that rule, Ada still gives you control over whether things dispatch, on a call-by-call basis. So, I view "tagged" as simply an efficiency hack. Efficiency is a Good Thing, too, though. >Yes, it's called Eiffel and it's great having such flexibility! :-). One aspect >of that flexibility is that, if, one sunny day, you decide you need to extend a >type you go right in and do it and don't have to touch the original. Well, that's not *really* true. You quite often have to go back and change the original, because you're doing something new that conflicts with assumptions made by the original. >... In Ada, you >have to go back and redefine the type to make it tagged. The impact of this may >be limited to that - don't know, haven't thought about it - but you shouldn't have >to redefine something to reuse it. I pretty much agree with that. You can, of course, make all your types tagged in the first place, and then you won't have that problem. >: Whether or not a type >:should be tagged is a DESIGN decision; I totally disagree that it >:should be an implementation decision. By the way a compiler can not >:in general determine if the "type is extended elsewhere" since in >:Ada extensions can appear in other compilation units. > >I can't think offhand of a suitable way of dealing with this but that isn't to >say it's impossible. It is clearly possible to do at link time, and clearly impossible at compile time, given separate compilation. >You don't have to do it that way. You can use (for example) a synonym construct like > > procedure P, Q (X: T) is begin R(X); end P, Q; > >and redefine Q for extensions of X. Yes, that's another reasonable way to do it. - Bob ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` Choice of OO primitives in Ada95 Ray Toal 1996-02-21 0:00 ` Don Harrison @ 1996-02-22 0:00 ` Bernd Holzmueller 1996-02-23 0:00 ` Robert A Duff 1 sibling, 1 reply; 27+ messages in thread From: Bernd Holzmueller @ 1996-02-22 0:00 UTC (permalink / raw) Ray Toal wrote: > > donh@syd.csa.com.au (Don Harrison) wrote: > > >1) Why should you have to specify that a type is 'tagged'? Can't the compiler work > >that out for itself? eg. by seeing whether the type is extended elsewhere. The > >developer is forced to worry about what should be an implementation issue. > > Requiring 'tagged' is a GOOD THING! Inheritance weakens encapsulation. > The default case (no tagged) is that you design a type, and you > provide all and only those operations that work on the type and you > don't make any details available to anyone else - in short you > fully control the type. Now if your intent is to ALLOW derivation > then you must mark it tagged. This alerts the reader that this > type may be derived from. Imagine a language in which you could > inherit from any type you wanted to! :-) Whether or not a type > should be tagged is a DESIGN decision; I totally disagree that it > should be an implementation decision. By the way a compiler can not > in general determine if the "type is extended elsewhere" since in > Ada extensions can appear in other compilation units. Actually, the compiler _can_ work out if a tag is necessary. This is the case only if a type is used polymorphically, i.e., class-wide in Ada 95 terminology (and has _nothing_ to do with type-extension). A different implementation model than is given in the LRM would create a tag only in these cases, which would allow 'normal' (monomorphic) uses of a tagged type be as efficient as the use of an untagged type. The consequence is that a special kind of "tagged types" is not necessary if efficiency is of concern. This is discussed in more detail in a paper at Ada-Europe 96. Regarding the requirement to mark a type tagged to control type extension: I cannot see any advantage in restricting the flexibility (in the sense of potential reuse) of the language without gaining anything. Whether or not a programmer is going to use a type for type extension is not relevant for the author of the original type and should therefore not explicitly prohibitable by him. It could be of interest for a _user_ of the original type because dispatching can be a concern, but not necessarily because he may wish to use this type only monomorphically, and thus no dispatching will ever occur. Bernd -- ----------------------------------------------------------------------------- Bernd Holzmueller, Institute of Computer Science, University of Stuttgart email : holzmuel@informatik.uni-stuttgart.de http://www.informatik.uni-stuttgart.de/ifi/ps/bernd.html ----------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-22 0:00 ` Bernd Holzmueller @ 1996-02-23 0:00 ` Robert A Duff 0 siblings, 0 replies; 27+ messages in thread From: Robert A Duff @ 1996-02-23 0:00 UTC (permalink / raw) In article <312C236B.2344@informatik.uni-stuttgart.de>, Bernd Holzmueller <holzmuel@informatik.uni-stuttgart.de> wrote: >Actually, the compiler _can_ work out if a tag is necessary. ... Are you saying that if I have a program that contains no dispatching at all (i.e. nothing is ever converted to a class-wide type), then it is possible for the compiler to avoid ever storing tags? I don't see how that can be true, because of re-dispatching. Consider: package P1 is type T1 is tagged null record; procedure P(X: T1); procedure Q(X: T1); end P1; with P1; use P1; package P2 is type T2 is new T1 with null record; -- Override Q, but not P: procedure Q(X: T2); end P2; with P1; use P1; with P2; use P2; procedure Client is Object: T2; begin P(T1(X)); -- This is *not* a dispatching call. end Client; I claim that the tag of Object (namely the tag representing type T2) must be made available to procedure P1.P. The tag could be stored as a hidden component of Object, or it could be passed as an extra parameter to P. Either way, there is some overhead in doing this. Now, suppose there's no dispatching anywhere in the program. For example, P might look like this: procedure P(X: T1) is begin Q(X); -- This is *not* a dispatching call. end P; Then we've wasted some time and space by making that tag available to P. Distributed overhead. So, what if we eliminate that tag? Well, then if P looks like this: procedure P(X: T1) is begin Q(T1'Class(X)); -- This *is* a dispatching call. end P; it won't work, because the above call is supposed to call the version of Q for T2. But the compiler cannot know, at compile time of Client, what the body of P looks like. Therefore, it *must* pass that information to P, just *in case* P does a dispatching call. QED. Unless, of course, you're willing to generate code at link time. - Bob ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` Don Harrison 1996-02-20 0:00 ` Jon S Anthony 1996-02-20 0:00 ` Choice of OO primitives in Ada95 Ray Toal @ 1996-02-23 0:00 ` Robert A Duff 2 siblings, 0 replies; 27+ messages in thread From: Robert A Duff @ 1996-02-23 0:00 UTC (permalink / raw) In article <Dn22M0.G0D@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >Robert A Duff writes: >:No, they don't have to be in the same package (and as you say, probably >:should not be). Could you explain what you mean -- why do you think >:that all of the above types have to be in the same package? > >I don't have an RM available but quoting you (25.1.96) on the subject of dispatching >operations in response to Arcadio A. Sincero: > >> >As a matter of fact, the only indication that TPerson's Walk "belongs to >> >it" is that TPerson's Walk has a TPerson parameter. > >> That, plus the fact that it's in the same package. > >So, if you want all of the operations to be dispatching (primitive), then the >tagged types must also be in the same package. Sorry, you misunderstood me. In Ada, a procedure can only be dispatching on *one* type. It can have several parameters of that type, and a result of that type (if it's a function). But there is a run-time check that all of those parameters have the same type tag. Ada does not have multi-dispatching, like CLOS does. But neither to any of the languages (such as Eiffel) that use the prefix notation. Now a procedure that's dispatching on type T can have other parameters of other types. It does not need to be in the same package as those other types. Same thing in Eiffel -- the operation is inside one class, and is dispatching (only) on that class, but can have parameters of some other class. So, typically, you put exactly one type in a package, and put its primitive (dispatching) operations in that same package. You can put operations that don't dispatch on that type in some other package. The one case where this doesn't work too well is when you have two types that are defined in terms of each other (e.g. T1 contains a pointer to T2, and T2 contains a pointer to T1). When this happens, you are forced to put both types in the same package, or use one of the slightly ugly workarounds that have been discussed here recently. I admit that is a language design flaw. But don't generalize that to say that all types in your entire program have to get sucked into the same package! >In the same thread, Jon S Anthony went on to say that non-dispatching operations >using tagged types were those defined in different packages to those types: > >> Just to add one more bit (completely beating it to death...), it is also >> legal to have a subprogram with operands of both types as long as it is >> not in the package where the two types are declared. Of course such a >> subprogram is not primitive and will never dispatch (assuming it has no >> other controlling operands). > >Dispatching or not dispatching depending on where an operation is defined is not >what you would call consistent. Heh? I thought you were arguing in favor of the class-based languages, which do exactly that. In Eiffel, an operation is dispatching based on which class it's in. In CLOS, you can have dispatching operations that are declared whereever you want. That can lead to some rather disorganized code, but I suppose it's necessary if you're going to have multi-methods. Anyway, I think it's best to gather the dispatching operations together with the type (either inside it, as in Eiffel, or in the same package, as in Ada). Note also that in Eiffel you can declare that a given operation cannot be further overridden ("frozen", or something like that?). That's a very similar feature to what you complain about here -- in part, it is a hint to the compiler that calling that function won't dispatch to someplace else, and in part, it's a help in understanding the program, because you can know exactly which function is being called. >A couple of other gripes: > >1) Why should you have to specify that a type is 'tagged'? Can't the compiler work >that out for itself? eg. by seeing whether the type is extended elsewhere. The >developer is forced to worry about what should be an implementation issue. No, the compiler cannot tell, because of separate compilation. It can't tell in the Eiffel case, either, for the same reason. But in Eiffel, everything's dispatching. If you're willing to do extra work at link time, then you can optimize away the tag field. But not at compile time. Note that C++ has essentially the same thing -- the "virtual" keyword. If there are no virtual functions in a class, then no tag field is necessary. In Ada, if there is no "tagged" keyword, then no tag field is necessary. (One difference between Ada and C++ is that in Ada you cannot extend an untagged type with extra record components, whereas as you *can* do that in C++ (i.e. you can extend a class that has no virtual functions. There was a big fight about that during the design of Ada 9X. I don't like the way it turned out). So, you're pretty much correct that "tagged" is not a logical necessity. It is, in fact, an efficiency hack. >2) Similarly, why should the developer have to specify that an operation dispatches >(classwide operations)? Presumably, you're aiming for quicker execution, but >compilers could perform a certain degree of optimisation eg. If it knows the type >is not extended anywhere, there is no need to dispatch. There would also be >situations where the specific variant of an inherited type is known eg. following >an explicit assignment from an entity of that type. The reason for this is to avoid what is sometimes called the "fragile base class" problem that occurs in many large OO programs. If *everything* is potentially dispatching, it's much harder to understand the program. Better to do something special if you want dispatching. Another reason is of course upward compatibility. Ada 83 subprograms did not do dispatching, and of course you don't want all your Ada 83 programs to drastically change their behavior when compiled by an Ada 95 compiler. I suppose quicker execution is part of the reason, but it's not the main thing. Another point about the design of Ada's OOP: It was essential that there be little or no distributed overhead. That is, if you don't use the feature, your program should not be slowed down by the mere existence of the feature. And if you recompile an Ada 83 program with an Ada 95 compiler, it shouldn't get slower (assuming the Ada 95 compiler is as high quality as the Ada 83 one). If dispatching always happened, there would be distributed overhead (both space and time), that could not be eliminated without link-time optimizations. - Bob ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-19 0:00 ` Choice of OO primitives in Ada95 Don Harrison 1996-02-19 0:00 ` Norman H. Cohen 1996-02-19 0:00 ` Robert A Duff @ 1996-02-21 0:00 ` Robert I. Eachus 2 siblings, 0 replies; 27+ messages in thread From: Robert I. Eachus @ 1996-02-21 0:00 UTC (permalink / raw) In article <Dn0FqH.8Cw@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > This is an example of what I mean by tight-coupling of > abstractions - you are forced to encapsulate both abstractions > into a single module. Not FORCED to, choose to. > An example might best illustrate what I mean. Extending your > example and assuming each type is tagged, we might have: > package X is > type MARRIAGE is tagged ... > type PERSON is tagged ... > type UNIVERSITY is tagged ... > type GOVERNMENT is tagged ... > type ELECTORATE is tagged ... > type ADDRESS is tagged ... > procedure Enrol (Student : in out PERSON; Uni : in out UNIVERSITY); > procedure Award_Grant (Polies : in out GOVERNMENT; Uni : in out UNIVERSITY); > procedure Make_Gerrymander (Polies : in out GOVERNMENT; Elect : in out ELECTORATE); > function Office_Address (Elect : in ELECTORATE) return ADDRESS; > end X; > Each abstraction is related to the previous one but isn't > necessarily related to any other. As I understand it, the language > rules dictate that each of these abstractions must be in the same > package. But many of them have nothing to do with each other. They > are tightly-coupled even though they should be distinct and this > has been forced by the language rules. What language rules say this? The only thing remotely close is that when you have two types where you want primitive operations of one with operands of the other, you have to chose one of several approaches. ONE of those approaches is to package the types together, and if they are closely related, it is usually the right choice. Other choices include using child packages, making the operations on one type class-wide operations using a parent of the other type, treating the relation between the two types as its own class, generics, etc... But how would you attack this "problem" in C++? The "solutions" I have seen all have the same semantic effect as the Ada solution shown even though the file layout is usually different. (The mingling of the types is via #includes.) -- Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 [not found] ` <4g2f8v$15lc@watnews1.watson.ibm.com> 1996-02-19 0:00 ` Choice of OO primitives in Ada95 Don Harrison @ 1996-02-21 0:00 ` John DiCamillo 1996-02-22 0:00 ` Don Harrison 1 sibling, 1 reply; 27+ messages in thread From: John DiCamillo @ 1996-02-21 0:00 UTC (permalink / raw) ncohen@watson.ibm.com (Norman H. Cohen) writes: >|> Tucker Taft wrote: >|> : ... The orthogonality of >|> : types and modules in Ada 95 can be a major advantage, >|> : gives the programmer more flexibility in structuring their system But Cardelli and Wegner [CW85] showed that the opposite is true: providing package types through existential quantification is *more* flexible than having typeless package values as Ada provides. It also simplifies and unifies the concept of Ada generic packages, which are almost package types, and allows package values to be treated as first-class values. To be sure, C&W based their analysis on a type system supporting first class functional values (not present in Ada), so their conclusions may not hold for Ada. However, I find it difficult to believe that the converse is actually true. >|> What flexibility does this approach offer over conventionally encapsulated classes? >1. It allows other forms of encapsulation and packaging, not involving > classes. We could have a package providing a nonextendible private > type for complex numbers, deferred constants zero, one, and i, and a > set of arithmetic operations. We could have a package providing a set > of constants. We could have a package providing a set of > higher-level auxiliary routines operating on a type provided by > another package, building on the primitive operations provided by that > package. I've seen people try to use C++ classes with no nonstatic > members for these other forms of encapsulation and packaging, and the > result is strained. "Strained" in what way? _D&E_ [Stroustrup94] mentions several minor problems with this approach, but seems to be considering using classes to wrap already existing global declarations. (btw, i'm not disagreeing, just asking for clarification) > That's one reason C++ has added namespaces, which > are like Ada packages. Sort of... Namespaces simply control name visibility, they are not an abstraction mechanism. Namespaces do not have private parts. Namespaces may be extended by multiple declarations. > But once the full generality of Ada-like > packages is available, the class program unit as an encapsulation > mechanism is redundant. Again, sort of. Why talk of adding a separate class program unit? Why not just extend packages to provide package types? That way, you have only one mechanism for encapsulation (the package) and packages become first class values, allowing for more flexible package declarations and increased code-reuse. [big snip] >Ada certainly recognizes a close relationship among packages, types, and >operations: If a package provides both a type and subprograms with >parameters or results of that type, those subprograms "belong" to the >type. (In the technical jargon, they are "primitive operations" of the >type.) >But a package is a module, not a type. Which is the whole problem, despite what the rationale tries to claim, IMHO. Ada, despite being a very type-centric programming language, has too limited a concept of types. >The unnatural modeling of >one-per-class data items as static "members" in C++ comes from an attempt >to force-fit the two concepts into a single construct. Not at all. The unnatural modeling comes from a lack of class values in C++. This same problem causes the awkward semantics of constructors and destructors. In other words, static members are a C++ problem, not a class problem. >-- >Norman H. Cohen ncohen@watson.ibm.com -- ciao, milo ================================================================ John DiCamillo Fiery the Angels Fell milod@netcom.com Deep thunder rode around their shores ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-21 0:00 ` John DiCamillo @ 1996-02-22 0:00 ` Don Harrison 1996-02-24 0:00 ` Robert A Duff 0 siblings, 1 reply; 27+ messages in thread From: Don Harrison @ 1996-02-22 0:00 UTC (permalink / raw) John DiCamillo wrote: [objective arguments about class encapsulation] Thank you. I was beginning to wonder whether there was anyone else in this forum willing to discard their preconceived ideas and able to think objectively on this subject. Don. ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-22 0:00 ` Don Harrison @ 1996-02-24 0:00 ` Robert A Duff 0 siblings, 0 replies; 27+ messages in thread From: Robert A Duff @ 1996-02-24 0:00 UTC (permalink / raw) In article <Dn5K6K.8zI@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >John DiCamillo wrote: > >[objective arguments about class encapsulation] > >Thank you. I was beginning to wonder whether there was anyone else in this forum >willing to discard their preconceived ideas and able to think objectively on >this subject. Now, now. We were having a nice technical discussion, and then you go and insult everybody who happens to disagree with you. Yes, it *is* insulting to say that because I disagree with you, I'm stuck on preconceived ideas, and I am unable to think objectively. Grr. Is this whole thing just a troll, as somebody else suggested elsewhere in this thread? Do you want it to devolve into the usual religious language flame war, complete with ad-hominem arguments? If so, I don't particularly want to participate. - Bob ^ permalink raw reply [flat|nested] 27+ messages in thread
[parent not found: <4fmrhk$7k3@erinews.ericsson.se>]
* Re: Choice of OO primitives in Ada95 [not found] <4fmrhk$7k3@erinews.ericsson.se> @ 1996-02-19 0:00 ` Richard A. O'Keefe 0 siblings, 0 replies; 27+ messages in thread From: Richard A. O'Keefe @ 1996-02-19 0:00 UTC (permalink / raw) ehsjony@ehs.ericsson.se (Jonas Nygren) writes: >A possible syntax could be outlined as: > class type Class_Name [extends Super] is ... > >then we would not have the difficult to master freezing rules >and controlling operand rules. I for one am very happy with the Ada 95 model. It really says a lot for the Ada 83 design that OOP could be grafted in so cleanly. The freezing rules are pretty intuitive (note that C also has rules about when a type must be completed). >One would simply prefix with >the 'controlling operand' as for task and protected types. This is not a simplification. It is a complexification, because it forces every operation to have only one controlling operand. Set operations are the classic example when you have two operands of the same type (union, and so on). Dynamic dispatching is still a form of procedure call; why impose new syntax when we already have comprehensible syntax that works fine? Having used Pop-2, where f(x) x.f x, f() x, f.apply and so on all did the same thing, I can no longer get too enthusiastic about minor differences in syntax. See a recent discussion in comp.lang.dylan (which allows both dotted and dotless method calls) for why dotless might be preferred. -- Election time; but how to get Labour _out_ without letting Liberal _in_? Richard A. O'Keefe; http://www.cs.rmit.edu.au/~ok; RMIT Comp.Sci. ^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~1996-02-26 0:00 UTC | newest] Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <DMqHqF.9F1.0.-s@inmet.camb.inmet.com> [not found] ` <DMu9yw.5ts@assip.csasyd.oz> [not found] ` <4g2f8v$15lc@watnews1.watson.ibm.com> 1996-02-19 0:00 ` Choice of OO primitives in Ada95 Don Harrison 1996-02-19 0:00 ` Norman H. Cohen 1996-02-19 0:00 ` Robert A Duff 1996-02-20 0:00 ` Don Harrison 1996-02-20 0:00 ` Jon S Anthony 1996-02-22 0:00 ` Real OO (was Choice of OO primitives in Ada95) Don Harrison 1996-02-22 0:00 ` Jon S Anthony 1996-02-22 0:00 ` Robert Dewar 1996-02-23 0:00 ` Gene Ouye 1996-02-26 0:00 ` James O'Connor 1996-02-26 0:00 ` Gene Ouye 1996-02-24 0:00 ` Valery Croizier 1996-02-24 0:00 ` Robert A Duff 1996-02-26 0:00 ` Matthew B. Kennel 1996-02-26 0:00 ` Don Harrison 1996-02-26 0:00 ` So called Real OO (was blah blah blah...) Jon S Anthony 1996-02-20 0:00 ` Choice of OO primitives in Ada95 Ray Toal 1996-02-21 0:00 ` Don Harrison 1996-02-23 0:00 ` Robert A Duff 1996-02-22 0:00 ` Bernd Holzmueller 1996-02-23 0:00 ` Robert A Duff 1996-02-23 0:00 ` Robert A Duff 1996-02-21 0:00 ` Robert I. Eachus 1996-02-21 0:00 ` John DiCamillo 1996-02-22 0:00 ` Don Harrison 1996-02-24 0:00 ` Robert A Duff [not found] <4fmrhk$7k3@erinews.ericsson.se> 1996-02-19 0:00 ` Richard A. O'Keefe
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox