* 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; 16+ 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] 16+ messages in thread
[parent not found: <DMqHqF.9F1.0.-s@inmet.camb.inmet.com>]
[parent not found: <DMu9yw.5ts@assip.csasyd.oz>]
[parent not found: <4g2f8v$15lc@watnews1.watson.ibm.com>]
* 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; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-19 0:00 ` 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; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-19 0:00 ` 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; 16+ 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] 16+ 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 ` Ray Toal ` (2 more replies) 0 siblings, 3 replies; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` Don Harrison @ 1996-02-20 0:00 ` Ray Toal 1996-02-21 0:00 ` Don Harrison 1996-02-22 0:00 ` Bernd Holzmueller 1996-02-20 0:00 ` Jon S Anthony 1996-02-23 0:00 ` Robert A Duff 2 siblings, 2 replies; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` 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; 16+ 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] 16+ 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; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 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 1 sibling, 1 reply; 16+ 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] 16+ 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; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` Don Harrison 1996-02-20 0:00 ` Ray Toal @ 1996-02-20 0:00 ` Jon S Anthony 1996-02-23 0:00 ` Robert A Duff 2 siblings, 0 replies; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-20 0:00 ` Don Harrison 1996-02-20 0:00 ` Ray Toal 1996-02-20 0:00 ` Jon S Anthony @ 1996-02-23 0:00 ` Robert A Duff 2 siblings, 0 replies; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 1996-02-19 0:00 ` 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; 16+ 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] 16+ messages in thread
* Re: Choice of OO primitives in Ada95 [not found] ` <4g2f8v$15lc@watnews1.watson.ibm.com> 1996-02-19 0:00 ` Don Harrison @ 1996-02-21 0:00 ` John DiCamillo 1996-02-22 0:00 ` Don Harrison 1 sibling, 1 reply; 16+ 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] 16+ 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; 16+ 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] 16+ 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; 16+ 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] 16+ messages in thread
end of thread, other threads:[~1996-02-24 0:00 UTC | newest] Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <4fmrhk$7k3@erinews.ericsson.se> 1996-02-19 0:00 ` Choice of OO primitives in Ada95 Richard A. O'Keefe [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 ` 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 ` 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-20 0:00 ` Jon S Anthony 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
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox