* Re: Real OO [not found] <4id031$cf9@dayuc.dayton.saic.com> @ 1996-03-18 0:00 ` Norman H. Cohen 1996-03-18 0:00 ` The Right Reverend Colin James III 1996-03-21 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) Ulrich Windl 1996-03-20 0:00 ` Real OO Don Harrison ` (3 subsequent siblings) 4 siblings, 2 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-03-18 0:00 UTC (permalink / raw) In article <4id031$cf9@dayuc.dayton.saic.com>, John G. Volan <John_Volan@ccmail.dayton.saic.com> writes (concerning the division of a tagged type's interfaces into multiple subinterfaces for different clients): |> Here's an alternative approach that is subtly -- but significantly -- |> different: Make the subprograms in the child packages _classwide_ |> operations, and have them _dispatch_ to the private primitives: |> |> package Abstraction_1 is |> type T is tagged null record; |> private |> procedure Op_1 (X: in out T); -- for Specialty_1 |> procedure Op_2 (X: in out T); -- for Specialty_2 |> end Abstraction_1; |> |> |> package Abstraction_1.Specialty_1 is |> procedure Op_1 (X: in out T'Class); -- classwide operation |> pragma Inline (Op_1); |> end Abstraction_1.Specialty_1; |> |> package body Abstraction_1.Specialty_1 is |> procedure Op_1 (X: in out T'Class) is |> begin |> Abstraction_1.Op_1 (X); -- dispatching call |> end Op_1; |> end Abstraction_1.Specialty_1; ... |> package Abstraction_1.Specialty_2 is |> procedure Op_2 (X: in out T'Class); -- classwide operation |> pragma Inline (Op_2); |> end Abstraction_1.Specialty_2; |> |> package body Abstraction_1.Specialty_2 is |> procedure Op_2 (X: in out T'Class) is |> begin |> Abstraction_1.Op_2 (X); -- dispatching call |> end Op_2; |> end Abstraction_1.Specialty_2; Yes, this is an excellent paradigm! Extending T means that there is more than one kind of T, but that the two subinterfaces are designed to perform particular functions with any kind of T that comes along. The fact that there is more than one kind of T is irrelevant to the users of these packages, as long as Op_1 or Op_2 does the appropriate thing for any kind of T that is encountered. Defining "the right thing" is the responsibility of the programmer extending T. ... |> (In fact, I'd say in general that any operation ought to be classwide if |> it isn't explicitly a primitive. IMHO, only under very rare |> circumstances should a non-primitive operation restrict a parameter to |> accept only a specific root type but not any of its derived types.) By instinct, I'm wary of such sweeping generalizations. However, it would appear that John's approach is the appropriate one in many circumstances. ... |> (Well, this whole discussion might be a lot clearer if we had a more |> concrete example that exhibited this kind of pattern. Ideas anyone?) My canonical example of an abstraction that has different interfaces for different clients is a device driver, or more precisely, an abstract device state. There are certain operations meant to be invoked from interrupt handlers as the result of events in a device; there are other operations meant to be invoked from application programs using the device. One can define an abstract tagged type for device states, together with dispatching operations, in a parent package. Separate child packages, one providing an interface for interrupt handlers and one providing an interface for applications, can define classwide versions of those operations appropriate for each interface. These classwide versions simply invoke the corresponding dispatching operation of the abstract tagged type. For each kind of phyiscal device, one derives from the abstract type, overriding all its operations. Given an object corresponding to a particular kind of physical device, a client (either an interrupt handler or an application) continues to simply invoke the corresponding classwide operation, which dutifully makes a dispatching call that dispatches to the overriding subprogram defined for the appropriate derived type. This isolates clients from the details of physical devices and makes it unnecessary to modify clients when new physical devices arise. (Of course a new physical device may make a new kind of abstract operation possible. This will be defined as a primitive operation of the corresponding derived type. Children of the packages providing interrupt-handler and client interfaces can provide corresponding classwide operations for use by clients.* Thus application clients CAN be modified to take advantage of the special characteristics of a new physical device, as a result of a deliberate decision to upgrade the application. However, this is not NECESSARY: A client that was working before the new physical device was added will continue working, so the client can be upgraded at our leisure or not at all.) *-To avoid the need for clients to become aware of whether a given abstract device supports the new operation, the classwide operation should accept a parameter of the classwide type of the original root abstract type and check whether the object belongs to a class for which dispatching is possible. If not, the classwide type should provide an appropriate response--perhaps an expensive emulation of the new operation using the old operations common to all devices. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-18 0:00 ` Real OO Norman H. Cohen @ 1996-03-18 0:00 ` The Right Reverend Colin James III 1996-03-19 0:00 ` Norman H. Cohen 1996-03-21 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) Ulrich Windl 1 sibling, 1 reply; 218+ messages in thread From: The Right Reverend Colin James III @ 1996-03-18 0:00 UTC (permalink / raw) ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: | In article <4id031$cf9@dayuc.dayton.saic.com>, John G. Volan | <John_Volan@ccmail.dayton.saic.com> writes (concerning the division of a | tagged type's interfaces into multiple subinterfaces for different | clients): | | |> Here's an alternative approach that is subtly -- but significantly -- | |> different: Make the subprograms in the child packages _classwide_ | |> operations, and have them _dispatch_ to the private primitives: | |> | |> package Abstraction_1 is | |> type T is tagged null record; | |> private | |> procedure Op_1 (X: in out T); -- for Specialty_1 | |> procedure Op_2 (X: in out T); -- for Specialty_2 | |> end Abstraction_1; | |> | |> | |> package Abstraction_1.Specialty_1 is | |> procedure Op_1 (X: in out T'Class); -- classwide operation | |> pragma Inline (Op_1); | |> end Abstraction_1.Specialty_1; | |> | |> package body Abstraction_1.Specialty_1 is | |> procedure Op_1 (X: in out T'Class) is | |> begin | |> Abstraction_1.Op_1 (X); -- dispatching call | |> end Op_1; | |> end Abstraction_1.Specialty_1; | ... | |> package Abstraction_1.Specialty_2 is | |> procedure Op_2 (X: in out T'Class); -- classwide operation | |> pragma Inline (Op_2); | |> end Abstraction_1.Specialty_2; | |> | |> package body Abstraction_1.Specialty_2 is | |> procedure Op_2 (X: in out T'Class) is | |> begin | |> Abstraction_1.Op_2 (X); -- dispatching call | |> end Op_2; | |> end Abstraction_1.Specialty_2; | | Yes, this is an excellent paradigm! Extending T means that there is more | than one kind of T, but that the two subinterfaces are designed to | perform particular functions with any kind of T that comes along. The | fact that there is more than one kind of T is irrelevant to the users of | these packages, as long as Op_1 or Op_2 does the appropriate thing for | any kind of T that is encountered. Defining "the right thing" is the | responsibility of the programmer extending T. | | ... | |> (In fact, I'd say in general that any operation ought to be classwide if | |> it isn't explicitly a primitive. IMHO, only under very rare | |> circumstances should a non-primitive operation restrict a parameter to | |> accept only a specific root type but not any of its derived types.) | | By instinct, I'm wary of such sweeping generalizations. However, it | would appear that John's approach is the appropriate one in many | circumstances. | | ... | |> (Well, this whole discussion might be a lot clearer if we had a more | |> concrete example that exhibited this kind of pattern. Ideas anyone?) | | My canonical example of an abstraction that has different interfaces for | different clients is a device driver, or more precisely, an abstract | device state. There are certain operations meant to be invoked from | interrupt handlers as the result of events in a device; there are other | operations meant to be invoked from application programs using the | device. | | One can define an abstract tagged type for device states, together with | dispatching operations, in a parent package. Separate child packages, | one providing an interface for interrupt handlers and one providing an | interface for applications, can define classwide versions of those | operations appropriate for each interface. These classwide versions | simply invoke the corresponding dispatching operation of the abstract | tagged type. | | For each kind of phyiscal device, one derives from the abstract type, | overriding all its operations. Given an object corresponding to a | particular kind of physical device, a client (either an interrupt handler | or an application) continues to simply invoke the corresponding classwide | operation, which dutifully makes a dispatching call that dispatches to | the overriding subprogram defined for the appropriate derived type. This | isolates clients from the details of physical devices and makes it | unnecessary to modify clients when new physical devices arise. | | (Of course a new physical device may make a new kind of abstract | operation possible. This will be defined as a primitive operation of the | corresponding derived type. Children of the packages providing | interrupt-handler and client interfaces can provide corresponding | classwide operations for use by clients.* Thus application clients CAN be | modified to take advantage of the special characteristics of a new | physical device, as a result of a deliberate decision to upgrade the | application. However, this is not NECESSARY: A client that was working | before the new physical device was added will continue working, so the | client can be upgraded at our leisure or not at all.) | | *-To avoid the need for clients to become aware of whether a given | abstract device supports the new operation, the classwide operation | should accept a parameter of the classwide type of the original root | abstract type and check whether the object belongs to a class for which | dispatching is possible. If not, the classwide type should provide an | appropriate response--perhaps an expensive emulation of the new | operation using the old operations common to all devices. | | -- | Norman H. Cohen ncohen@watson.ibm.com This has nothing to do with Eiffel, does it. So stop distributing your inane thread to comp.lang.eiffel. ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Colin James III, Principal Scientist cjames@cec-services.com CEC Services, 2080 Kipling St, Lakewood, CO 80215-1502 USA Voice: 303.231.9437; Facsimile: .231.9438; Data: .231.9434 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-18 0:00 ` The Right Reverend Colin James III @ 1996-03-19 0:00 ` Norman H. Cohen 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III 1996-03-21 0:00 ` Real OO Don Harrison 0 siblings, 2 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-03-19 0:00 UTC (permalink / raw) In article <314d98bf.562645731@news.dimensional.com>, cjames@melchizedek.cec-services.com (The Right Reverend Colin James III) writes: |> ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: ... |> This has nothing to do with Eiffel, does it. In fact it does. The topic is a comparison of Eiffel's selective export with corresponding features in Ada. |> So stop distributing your inane thread to comp.lang.eiffel. Colin, I recognize your right to disagree with me about what is and is not of interest to readers of comp.lang.eiffel. I do not recognize your self-appointed role as gatekeeper of that newsgroup. When you come across an article that is not of interest to you, I think you will find it takes far less energy to skip over it than to continue posting your "Refrain from posting" messages, and certainly far less energy than you expended on Friday to determine my chain of management and send an amusing 4-page fax to a vice president four levels up the chain. (While I'm sure that my manager appreciated your advice that "Cohen averages about 1-article per workday, ie, apparently Cohen needs more work assignments and tighter supervision," he seems to have reached a different conclusion. Perhaps your fax alleging inappropriate posting to Usenet groups would have had more credibility if it had not included a copy of your own posting telling me to go to hell.) -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Norman Cohen giving IBM a bad name 1996-03-19 0:00 ` Norman H. Cohen @ 1996-03-20 0:00 ` The Right Reverend Colin James III 1996-03-20 0:00 ` Dave Retherford ` (6 more replies) 1996-03-21 0:00 ` Real OO Don Harrison 1 sibling, 7 replies; 218+ messages in thread From: The Right Reverend Colin James III @ 1996-03-20 0:00 UTC (permalink / raw) ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: | In article <314d98bf.562645731@news.dimensional.com>, | cjames@melchizedek.cec-services.com (The Right Reverend Colin James III) | writes: | | |> ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: | ... | |> This has nothing to do with Eiffel, does it. | | In fact it does. The topic is a comparison of Eiffel's selective export | with corresponding features in Ada. | | |> So stop distributing your inane thread to comp.lang.eiffel. | | Colin, I recognize your right to disagree with me about what is and is | not of interest to readers of comp.lang.eiffel. I do not recognize your | self-appointed role as gatekeeper of that newsgroup. | | When you come across an article that is not of interest to you, I think | you will find it takes far less energy to skip over it than to continue | posting your "Refrain from posting" messages, and certainly far less | energy than you expended on Friday to determine my chain of management | and send an amusing 4-page fax to a vice president four levels up the | chain. (While I'm sure that my manager appreciated your advice that | "Cohen averages about 1-article per workday, ie, apparently Cohen needs | more work assignments and tighter supervision," he seems to have reached | a different conclusion. Perhaps your fax alleging inappropriate posting | to Usenet groups would have had more credibility if it had not included a | copy of your own posting telling me to go to hell.) | | -- | Norman H. Cohen ncohen@watson.ibm.com Thanks for your opinions, but the fact remains that your articles in the thread "Real OO" have nothing whatsoever to do with Eiffel, comp.lang.eiffel, or the official business of IBM (stock in which some readers of this may own). Stop what you're doing Cohen because it makes you and IBM look bad. ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Colin James III, Principal Scientist cjames@cec-services.com CEC Services, 2080 Kipling St, Lakewood, CO 80215-1502 USA Voice: 303.231.9437; Facsimile: .231.9438; Data: .231.9434 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Norman Cohen giving IBM a bad name 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III @ 1996-03-20 0:00 ` Dave Retherford 1996-03-20 0:00 ` Real OO Dale Stanbrough ` (5 subsequent siblings) 6 siblings, 0 replies; 218+ messages in thread From: Dave Retherford @ 1996-03-20 0:00 UTC (permalink / raw) In article <314f6cf8.682571966@news.dimensional.com>, The Right Reverend Colin James III <cjames@melchizedek.cec-services.com> wrote: > ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: > > | In article <314d98bf.562645731@news.dimensional.com>, > | cjames@melchizedek.cec-services.com (The Right Reverend Colin James III) > | writes: > | > | |> ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: > | ... > | |> This has nothing to do with Eiffel, does it. > | > | In fact it does. The topic is a comparison of Eiffel's selective export > | with corresponding features in Ada. > | > | |> So stop distributing your inane thread to comp.lang.eiffel. > | [delete...] > > Thanks for your opinions, but the fact remains that your articles in > the thread "Real OO" have nothing whatsoever to do with Eiffel, > comp.lang.eiffel, or the official business of IBM (stock in which some > readers of this may own). > > Stop what you're doing Cohen because it makes you and IBM look bad. > > ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ > Colin James III, Principal Scientist cjames@cec-services.com > CEC Services, 2080 Kipling St, Lakewood, CO 80215-1502 USA > Voice: 303.231.9437; Facsimile: .231.9438; Data: .231.9434 > ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Norm, Perhaps Colin is right (gasp). Afterall, from his diligence in posting to c.l.a to remove a discussion of OO from comp.lang.eiffel I can infer that either 1) Eiffel has nothing to due to with OOT ; or 2) CJIII has nothing to do with OOT; or 3) CJII wants nothing to due with OOT; or 4) CJII knows nothing about OOT and prefers to keep it that way ;-) Dave p.s. I still waiting for the local Book Stop to get you new book. Yes, this is a shameless plug (plead) to someone of influence to use said influence so I _can_get_a_copy_ of the book_! :-). -- ________________________________________________________________________ | Dave Retherford | | | Daver@Neosoft.com | "I've learned that eating chocolate | | 73313.2671@compuserve.com | won't solve your problems, but it | | djretherford@ccgate.hac.com | does't hurt anything either." | | (work) | -- unknown, age 28 | |________________________________|_______________________________________| ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III 1996-03-20 0:00 ` Dave Retherford @ 1996-03-20 0:00 ` Dale Stanbrough 1996-03-21 0:00 ` Richard Pitre 1996-03-20 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) David Emery ` (4 subsequent siblings) 6 siblings, 1 reply; 218+ messages in thread From: Dale Stanbrough @ 1996-03-20 0:00 UTC (permalink / raw) Thanks Cohen. I think what you're doing (contributing interesting points of view and information) makes you and IBM look good. Dale ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-20 0:00 ` Real OO Dale Stanbrough @ 1996-03-21 0:00 ` Richard Pitre 0 siblings, 0 replies; 218+ messages in thread From: Richard Pitre @ 1996-03-21 0:00 UTC (permalink / raw) In article <4iq34c$nvt@goanna.cs.rmit.EDU.AU> Dale Stanbrough <dale@goanna.cs.rmit.EDU.AU> writes: > Thanks Cohen. I think what you're doing (contributing interesting > points of view and information) makes you and IBM look good. > > > Dale Echo richard ^ permalink raw reply [flat|nested] 218+ messages in thread
* Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III 1996-03-20 0:00 ` Dave Retherford 1996-03-20 0:00 ` Real OO Dale Stanbrough @ 1996-03-20 0:00 ` David Emery 1996-03-20 0:00 ` Mark R Okern - F95 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name Brian & Karen Bell ` (3 subsequent siblings) 6 siblings, 1 reply; 218+ messages in thread From: David Emery @ 1996-03-20 0:00 UTC (permalink / raw) In article <314f6cf8.682571966@news.dimensional.com>, cjames@melchizedek.cec-services.com wrote: > ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: > > | In article <314d98bf.562645731@news.dimensional.com>, > | cjames@melchizedek.cec-services.com (The Right Reverend Colin James III) > | writes: > | > | |> ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: > | ... > | |> This has nothing to do with Eiffel, does it. > | > | In fact it does. The topic is a comparison of Eiffel's selective export > | with corresponding features in Ada. > | > | |> So stop distributing your inane thread to comp.lang.eiffel. > | > | Colin, I recognize your right to disagree with me about what is and is > | not of interest to readers of comp.lang.eiffel. I do not recognize your > | self-appointed role as gatekeeper of that newsgroup. > | > | When you come across an article that is not of interest to you, I think > | you will find it takes far less energy to skip over it than to continue > | posting your "Refrain from posting" messages, and certainly far less > | energy than you expended on Friday to determine my chain of management > | and send an amusing 4-page fax to a vice president four levels up the > | chain. (While I'm sure that my manager appreciated your advice that > | "Cohen averages about 1-article per workday, ie, apparently Cohen needs > | more work assignments and tighter supervision," he seems to have reached > | a different conclusion. Perhaps your fax alleging inappropriate posting > | to Usenet groups would have had more credibility if it had not included a > | copy of your own posting telling me to go to hell.) > | > | -- > | Norman H. Cohen ncohen@watson.ibm.com > > Thanks for your opinions, but the fact remains that your articles in > the thread "Real OO" have nothing whatsoever to do with Eiffel, > comp.lang.eiffel, or the official business of IBM (stock in which some > readers of this may own). > > Stop what you're doing Cohen because it makes you and IBM look bad. > > ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ > Colin James III, Principal Scientist cjames@cec-services.com > CEC Services, 2080 Kipling St, Lakewood, CO 80215-1502 USA > Voice: 303.231.9437; Facsimile: .231.9438; Data: .231.9434 > ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Thanks for your drivelling, but the fact remains that your rants in most threads in which you paticipate have nothing whatsoever to do with Eiffel, Ada, software engineering, U.S. law or reality. Stop what you're doing James because it makes you and humanity look bad. Dave (so sue me, for real!) Emery ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) 1996-03-20 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) David Emery @ 1996-03-20 0:00 ` Mark R Okern - F95 1996-03-20 0:00 ` Real OO John G. Volan 1996-03-21 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) Richard A. O'Keefe 0 siblings, 2 replies; 218+ messages in thread From: Mark R Okern - F95 @ 1996-03-20 0:00 UTC (permalink / raw) There is a book out titled "Minding Your Cybermanners on the Internet".... Perhaps it should be read...it only costs $15 or so, and will save the rest of us from many headaches! I was polite last time, but my (and I'm sure that other people's) patience is wearing quite thin when it comes to these kinds of threads. BTW, be thankful that somebody at IBM takes the time to participate in newsgroup discussions. I consider that to be an excellent form of customer support! For conservation of space, I have not repeated the posts which led me to speak up. This is all I will say, and I plan on ignoring any further threads of this type. Disclaimer: Everything above is my personal opinion, and in no way reflects the opinion of any person or institution with which I am affiliated. Public flames accepted, no faxes please. ************************************************ Mark R. Okern CS120 Tutor/NeXT Lab Assistant UW-LaCrosse Department of Computer Science 209B Hutchison (608) 789-2609 ************************************************ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-20 0:00 ` Mark R Okern - F95 @ 1996-03-20 0:00 ` John G. Volan 1996-03-21 0:00 ` Scott Leschke 1996-03-21 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) Richard A. O'Keefe 1 sibling, 1 reply; 218+ messages in thread From: John G. Volan @ 1996-03-20 0:00 UTC (permalink / raw) In article <4ik3j7$cl6@watnews1.watson.ibm.com> Norman H. Cohen, ncohen@watson.ibm.com writes: >In article <4id031$cf9@dayuc.dayton.saic.com>, John G. Volan ><John_Volan@ccmail.dayton.saic.com> writes (concerning the division of a >tagged type's interfaces into multiple subinterfaces for different >clients): ... >|> (In fact, I'd say in general that any operation ought to be classwide if >|> it isn't explicitly a primitive. IMHO, only under very rare >|> circumstances should a non-primitive operation restrict a parameter to >|> accept only a specific root type but not any of its derived types.) > >By instinct, I'm wary of such sweeping generalizations. However, it >would appear that John's approach is the appropriate one in many >circumstances. Of course it's prudent to be wary of generalization -- or rather, it's prudent to be wary of slavish devotion to generalization. What I should have said was: "If an operation isn't explicitly a primitive, then the default choice ought to be to make it classwide. Making it specific to the root type is still a possibility, but that choice should carefully reasoned, and the justifications for it should be documented." ------------------------------------------------------------------------ Internet.Usenet.Put_Signature ( Name => "John G. Volan", E_Mail => "John_Volan@dayton.saic.com", Favorite_Slogan => "Ada95: The *FIRST* International-Standard OOPL", Humorous_Disclaimer => "These opinions are undefined by SAIC, so" & "any use would be erroneous ... or is that a bounded error now?" ); ------------------------------------------------------------------------ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-20 0:00 ` Real OO John G. Volan @ 1996-03-21 0:00 ` Scott Leschke 1996-03-21 0:00 ` Norman H. Cohen ` (2 more replies) 0 siblings, 3 replies; 218+ messages in thread From: Scott Leschke @ 1996-03-21 0:00 UTC (permalink / raw) John G. Volan <John_Volan@ccmail.dayton.saic.com> writes: >Of course it's prudent to be wary of generalization -- or rather, it's >prudent to be wary of slavish devotion to generalization. What I should >have said was: "If an operation isn't explicitly a primitive, then the >default choice ought to be to make it classwide. Making it specific to >the root type is still a possibility, but that choice should carefully >reasoned, and the justifications for it should be documented." How does one declare syntactically that an operation is SPECIFIC to a type within a class, as opposed to being either a primitive of that type or class-wide? I've also wondered if there was any way to explicitly declare an operation as invariant within a class and hence, non-overridable. -- Scott Leschke.........................email: leschkes@cig.mot.com Motorola, Inc............................ph: 847-632-2786 1501 W Shure Drive......................fax: 847-632-3145 Arlington Heights, IL 60004......mailstop: 1301 ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-21 0:00 ` Scott Leschke @ 1996-03-21 0:00 ` Norman H. Cohen 1996-03-21 0:00 ` Robert A Duff 1996-03-22 0:00 ` Don Harrison 2 siblings, 0 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-03-21 0:00 UTC (permalink / raw) In article <leschkes.827424395@ferret>, leschkes@ferret.cig.mot.com (Scott Leschke) writes: |> How does one declare syntactically that an operation is SPECIFIC to a |> type within a class, as opposed to being either a primitive of that |> type or class-wide? One way is to declare the operation in a different package (either a physically nested package or a child package or an unrelated library package). Well, maybe that's three ways. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-21 0:00 ` Scott Leschke 1996-03-21 0:00 ` Norman H. Cohen @ 1996-03-21 0:00 ` Robert A Duff 1996-03-22 0:00 ` Don Harrison 2 siblings, 0 replies; 218+ messages in thread From: Robert A Duff @ 1996-03-21 0:00 UTC (permalink / raw) In article <leschkes.827424395@ferret>, Scott Leschke <leschkes@ferret.cig.mot.com> wrote: >How does one declare syntactically that an operation is SPECIFIC to a >type within a class, as opposed to being either a primitive of that >type or class-wide? If you declare it *not* in the same package, and it has a parameter of type T, then it's specific to T (not general for everything in T'Class). Jon Volan said this is usualy a bad idea, and I agree. >I've also wondered if there was any way to explicitly declare an operation >as invariant within a class and hence, non-overridable. Make it class-wide. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-21 0:00 ` Scott Leschke 1996-03-21 0:00 ` Norman H. Cohen 1996-03-21 0:00 ` Robert A Duff @ 1996-03-22 0:00 ` Don Harrison 2 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-03-22 0:00 UTC (permalink / raw) In article <leschkes.827424395@ferret>, leschkes@ferret.cig.mot.com (Scott Leschke) writes: : :John G. Volan <John_Volan@ccmail.dayton.saic.com> writes: : :>Of course it's prudent to be wary of generalization -- or rather, it's :>prudent to be wary of slavish devotion to generalization. What I should :>have said was: "If an operation isn't explicitly a primitive, then the :>default choice ought to be to make it classwide. Making it specific to :>the root type is still a possibility, but that choice should carefully :>reasoned, and the justifications for it should be documented." : :How does one declare syntactically that an operation is SPECIFIC to a :type within a class, as opposed to being either a primitive of that :type or class-wide? Assuming you mean 'specific to the root type of the class', by declaring the operation using the specific type (ie. without 'Class) somewhere else from the spec in which the type is declared. :I've also wondered if there was any way to explicitly declare an operation :as invariant within a class and hence, non-overridable. In Eiffel, you declare it as 'frozen'. Don't know whether Ada has an equivalent. :-- :Scott Leschke.........................email: leschkes@cig.mot.com :Motorola, Inc............................ph: 847-632-2786 :1501 W Shure Drive......................fax: 847-632-3145 :Arlington Heights, IL 60004......mailstop: 1301 Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) 1996-03-20 0:00 ` Mark R Okern - F95 1996-03-20 0:00 ` Real OO John G. Volan @ 1996-03-21 0:00 ` Richard A. O'Keefe 1996-03-21 0:00 ` Robert Dewar 1 sibling, 1 reply; 218+ messages in thread From: Richard A. O'Keefe @ 1996-03-21 0:00 UTC (permalink / raw) On page 22 of the February 1996 issue of Scientific American, there is a box "More Rules of the Road", concerning American laws that censor the Net. Here is something interesting: And if your e-mail, Usenet posting or World Wide Web page might be read by someone in Connecticut, be aware that it is a felony there to transmit text that contains threats with the intent to harass, annoy or alarm. ... It sounds as though anyone posting "say that one more time and I sue" on the net may be committing a felony in Connecticut if the article travels through that state, and if Paul Wallich, who wrote that snippet in SciAm, has his facts right. Perhaps someone with ready access to the laws of the state of Connecticut might like to verify this for us. Me, I'm going to be _very_ careful not to threaten anyone from now on, not even in jest, not even with smileys all over. -- The election is over, and Australia lost; the idjits elected _politicians_! Richard A. O'Keefe; http://www.cs.rmit.edu.au/~ok; RMIT Comp.Sci. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) 1996-03-21 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) Richard A. O'Keefe @ 1996-03-21 0:00 ` Robert Dewar 0 siblings, 0 replies; 218+ messages in thread From: Robert Dewar @ 1996-03-21 0:00 UTC (permalink / raw) Richard said "Me, I'm going to be _very_ careful not to threaten anyone from now on, not even in jest, not even with smileys all over." But Richard, then we will miss your colorful language. I will find this annoying, therefore I could regard the resolution you made above as an attempt to annoy me. Good thing I am not reading this in Connecticut :-) :-) ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Norman Cohen giving IBM a bad name 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III ` (2 preceding siblings ...) 1996-03-20 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) David Emery @ 1996-03-20 0:00 ` Brian & Karen Bell 1996-03-21 0:00 ` Kent Mitchell ` (2 subsequent siblings) 6 siblings, 0 replies; 218+ messages in thread From: Brian & Karen Bell @ 1996-03-20 0:00 UTC (permalink / raw) The Right Reverend Colin James III wrote: > > ncohen@watson.ibm.com (Norman H. Cohen) posted with deletions: > [snip] > > Stop what you're doing Cohen because it makes you and IBM look bad. >This may be the essential definition of 'surreal': "...incongruous arrangement and presentation of subject matter" Funk & Wagnell's Standard Desk Dictionary (1980) I mean "...it makes you...look bad"? Really? How do you suppose YOU look? > ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ > Colin James III, Principal Scientist cjames@cec-services.com > CEC Services, 2080 Kipling St, Lakewood, CO 80215-1502 USA > Voice: 303.231.9437; Facsimile: .231.9438; Data: .231.9434 > ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Norman Cohen giving IBM a bad name 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III ` (3 preceding siblings ...) 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name Brian & Karen Bell @ 1996-03-21 0:00 ` Kent Mitchell 1996-03-22 0:00 ` Robert Munck 1996-03-23 0:00 ` Tom Reid 6 siblings, 0 replies; 218+ messages in thread From: Kent Mitchell @ 1996-03-21 0:00 UTC (permalink / raw) The Right Reverend Colin James III (cjames@melchizedek.cec-services.com) wrote: : Thanks for your opinions, but the fact remains that your articles in : the thread "Real OO" have nothing whatsoever to do with Eiffel, : comp.lang.eiffel, or the official business of IBM (stock in which some : readers of this may own). : Stop what you're doing Cohen because it makes you and IBM look bad. I am actualy quite glad that Norm Cohen posts to c.l.a and other news groups. It's only by having people in companies like IBM and Rational (not that we're in the same class) reading the news groups that you can really find out what people are thinking out there. Sometime it's scary what they think ... and you know who you are ;-) -- Kent Mitchell | One possible reason that things aren't Technical Consultant | going according to plan is ..... Rational Software Corporation | that there never *was* a plan! ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Norman Cohen giving IBM a bad name 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III ` (4 preceding siblings ...) 1996-03-21 0:00 ` Kent Mitchell @ 1996-03-22 0:00 ` Robert Munck 1996-03-22 0:00 ` Mark Brennan 1996-03-23 0:00 ` Tom Reid 6 siblings, 1 reply; 218+ messages in thread From: Robert Munck @ 1996-03-22 0:00 UTC (permalink / raw) On Wed, 20 Mar 1996 02:33:48 GMT, cjames@melchizedek.cec-services.com (The Right Reverend Colin James III) wrote: >Stop what you're doing Cohen because it makes you and IBM look bad. > >~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ >Colin James III, Principal Scientist cjames@cec-services.com >CEC Services, 2080 Kipling St, Lakewood, CO 80215-1502 USA >Voice: 303.231.9437; Facsimile: .231.9438; Data: .231.9434 >~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ (Let me take this one.) Stop what you're doing Colin because it makes you and the following look bad: 1. Everyone in the world named Colin. 2. Ditto James. 3. Everyone whose father and grandfather had the same names as he. 4. Principals and Principles everywhere. 5. All Scientists. 6. Everyone on Kipling Street, in Lakewood, in Colorado, and in the USA. 7. Everyone with any of the digits 01258 or a dash in their zip code. 8. All Reverends. 9. Everyone on the right. 10. Everyone named Melchizedek. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Norman Cohen giving IBM a bad name 1996-03-22 0:00 ` Robert Munck @ 1996-03-22 0:00 ` Mark Brennan 1996-03-22 0:00 ` David Curry 0 siblings, 1 reply; 218+ messages in thread From: Mark Brennan @ 1996-03-22 0:00 UTC (permalink / raw) pp000166@interramp.com (Robert Munck): #On Wed, 20 Mar 1996 02:33:48 GMT, cjames@melchizedek.cec-services.com #(The Right Reverend Colin James III) wrote: # #Stop what you're doing Colin because it makes you and the following #look bad: # #1. Everyone in the world named Colin. #2. Ditto James. #8. All Reverends. And all lying kooks and crackpots :-) See http://www.wetware.com/mlegare/winnersk96.html ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Norman Cohen giving IBM a bad name 1996-03-22 0:00 ` Mark Brennan @ 1996-03-22 0:00 ` David Curry 0 siblings, 0 replies; 218+ messages in thread From: David Curry @ 1996-03-22 0:00 UTC (permalink / raw) Oh my gosh! It sounds like some people are getting sassy with the most terrible and all(most) (un)powerful CJIII... but what will become of you in the end? Perhaps if you offer homage and many sacrifices it will not be to late for you lest he destroy you in an avalanche of faxes and, in my opinion, the most stupid email messages ever composed by man or sent by machine. -- David Curry ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Norman Cohen giving IBM a bad name 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III ` (5 preceding siblings ...) 1996-03-22 0:00 ` Robert Munck @ 1996-03-23 0:00 ` Tom Reid 6 siblings, 0 replies; 218+ messages in thread From: Tom Reid @ 1996-03-23 0:00 UTC (permalink / raw) In article <314f6cf8.682571966@news.dimensional.com>, cjames@melchizedek.cec-services.com (The Right Reverend Colin James III) wrote: [...] >Thanks for your opinions, but the fact remains that your articles in >the thread "Real OO" have nothing whatsoever to do with Eiffel, >comp.lang.eiffel, or the official business of IBM (stock in which some >readers of this may own). > >Stop what you're doing Cohen because it makes you and IBM look bad. Colin: I would like to thank you (and I know every thinking person must agree) because: 1. Your parodies of correct net behavior are light, refreshing, and always in good taste. 2. You know how to present material in a constantly amusing fashion. 3. You are kind and ever gentle on those of lesser character and ability. 4. You remind us our lives are really not that boring and meaningless. 5. We now realize there are people who do have too much time on their hands. 6. You reaffirm our beliefs that the net should be open to those of all afflictions, including the reality impaired. I sure am glad that you have taken up the good fight against the net abusers. We need more people of your vision to keep it from falling into constant meaningful discussions of important subjects. Tom Reid ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-19 0:00 ` Norman H. Cohen 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III @ 1996-03-21 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-03-21 0:00 UTC (permalink / raw) I just want to explain why I crossposted this thread to groups other than comp.lang.ada. The subject matter concerns what might be regarded as essential features of an OO language. I initially posted it to comp.lang.ada but crossposted to comp.lang.eiffel and comp.object for the following reasons: 1) The features advocated are suggested on their own merit and may be considered independent of any specific language. They reflect, IMO, the essence of OO. Hence, the crossposting to comp.object. 2) They are epitomised in the current design of Eiffel (except for concurrency which is currently being implemented). Also, I thought the discussion would be more valuable if experienced Eiffelers presented their insights. Hence the crossposting to comp.lang.eiffel. I also considered crossposting to comp.lang.smalltalk but thought the discussion would degenerate into a pure OO versus hybrid OO talkfest and that the original topic would be lost. 3) In an attempt to rescue my integrity, having been accused of trolling. Clearly, it would have been wiser to crosspost from the beginning. That way I would have shown up-front that the post was serious. Certainly, from time to time, the content of the thread may sometimes be more specific to one language than the other but this is necessary to thrash out the issues. The overall subject has not been forgotten (by me, at least). Parties from both Eiffel and Ada groups have expressed interest in the thread. There's the option of a kill file for anyone finding it irrelevant. Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) 1996-03-18 0:00 ` Real OO Norman H. Cohen 1996-03-18 0:00 ` The Right Reverend Colin James III @ 1996-03-21 0:00 ` Ulrich Windl 1 sibling, 0 replies; 218+ messages in thread From: Ulrich Windl @ 1996-03-21 0:00 UTC (permalink / raw) In article <emery-1903962058280001@line130.nwm.mindlink.net> emery@grebyn.com (David Emery) writes: > > ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ > > Colin James III, Principal Scientist cjames@cec-services.com > > CEC Services, 2080 Kipling St, Lakewood, CO 80215-1502 USA > > Voice: 303.231.9437; Facsimile: .231.9438; Data: .231.9434 > > ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ For some reason I can't explain well, the above seems a candidate for a kill-list. I only wonder that ISE's WWW server references his name! > > Thanks for your drivelling, but the fact remains that your rants in > most threads in which you paticipate have nothing whatsoever to do > with Eiffel, Ada, software engineering, U.S. law or reality. > > Stop what you're doing James because it makes you and humanity look bad. > > Dave (so sue me, for real!) Emery ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO [not found] <4id031$cf9@dayuc.dayton.saic.com> 1996-03-18 0:00 ` Real OO Norman H. Cohen @ 1996-03-20 0:00 ` Don Harrison 1996-03-22 0:00 ` Don Harrison ` (2 subsequent siblings) 4 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-03-20 0:00 UTC (permalink / raw) John G. Volan writes: [example of selective export in Ada using classwide types etc.] Thanks for the example. Would like to go away and check my understanding of classwide types before offering my 2 cents worth. Otherwise Bob Duff will have to say 'I told you so' again with a string of smileys :-). Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO [not found] <4id031$cf9@dayuc.dayton.saic.com> 1996-03-18 0:00 ` Real OO Norman H. Cohen 1996-03-20 0:00 ` Real OO Don Harrison @ 1996-03-22 0:00 ` Don Harrison 1996-03-22 0:00 ` Norman H. Cohen ` (3 more replies) 1996-03-26 0:00 ` Jon S Anthony 1996-03-29 0:00 ` Joachim Durchholz 4 siblings, 4 replies; 218+ messages in thread From: Don Harrison @ 1996-03-22 0:00 UTC (permalink / raw) John G. Volan wrote: :Don Harrison, <donh@syd.csa.com.au> wrote: : :>Norman H. Cohen wrote: :> :>:In article <Do3Arr.4K5@assip.csasyd.oz>, donh@syd.csa.com.au :>:(Don Harrison) writes: :>: :>:|> One question: Assuming a tagged type is declared in Common_Interface and each of :>:|> the operations uses it as a parameter, is it true that only the operations of :>:|> Common_Interface (and not it's child units) are primitive? :>:|> If that is the case, then it would appear that Ada provides no means of defining :>:|> abstractions that are both inheritable and selectively export operations and :>:|> hinders reusability in this respect. :>: :>:This is a good point. If you want to divide the primitive operations of :>:a tagged type into several interfaces, the procedure is more involved: [Norman's example] :Here's an alternative approach that is subtly -- but significantly -- :different: Make the subprograms in the child packages _classwide_ :operations, and have them _dispatch_ to the private primitives: : : package Abstraction_1 is : type T is tagged null record; : private : procedure Op_1 (X: in out T); -- for Specialty_1 : procedure Op_2 (X: in out T); -- for Specialty_2 : end Abstraction_1; : : : package Abstraction_1.Specialty_1 is : procedure Op_1 (X: in out T'Class); -- classwide operation : pragma Inline (Op_1); : end Abstraction_1.Specialty_1; : : package body Abstraction_1.Specialty_1 is : procedure Op_1 (X: in out T'Class) is : begin : Abstraction_1.Op_1 (X); -- dispatching call : end Op_1; : end Abstraction_1.Specialty_1; : : : -- Abstraction_1.Specialty_1 is designed for the exclusive use of, : -- say, Client_1 (although this is not enforced by the language): : : with Abstraction_1.Specialty_1; : package Client_1 is ... : : -- Client_1 can invoke Abstraction_1.Specialty_1.Op_1 on any object in : -- the type-class rooted at Abstraction_1.T (i.e., any object of type : -- Abstraction_1.T or any type derived from that). : : : package Abstraction_1.Specialty_2 is : procedure Op_2 (X: in out T'Class); -- classwide operation : pragma Inline (Op_2); : end Abstraction_1.Specialty_2; : : package body Abstraction_1.Specialty_2 is : procedure Op_2 (X: in out T'Class) is : begin : Abstraction_1.Op_2 (X); -- dispatching call : end Op_2; : end Abstraction_1.Specialty_2; : : : -- Abstraction_1.Specialty_2 is designed for the exclusive use of, : -- say, Client_2 (although this is not enforced by the language): : : with Abstraction_1.Specialty_2; : package Client_2 is ... : : -- Client_2 can invoke Abstraction_1.Specialty_2.Op_2 on any object in : -- the type-class rooted at Abstraction_1.T (i.e., any object of type : -- Abstraction_1.T or any type derived from that). : : : -- Children of Abstraction_1 can derive new types from Abstraction_1.T : -- and override the private primitives: : : : package Abstraction_1.Abstraction_2 is : type T is new Abstraction_1.T with null record; : private : procedure Op_1 (X: in out T); -- overrides Abstraction_1.Op_1 : procedure Op_2 (X: in out T); -- overrides Abstraction_1.Op_2 : end Abstraction_1.Abstraction_2; : : : package Abstraction_1.Abstraction_3 is : type T is new Abstraction_1.T with null record; : private : procedure Op_1 (X: in out T); -- overrides Abstraction_1.Op_1 : procedure Op_2 (X: in out T); -- overrides Abstraction_1.Op_2 : end Abstraction_1.Abstraction_3; : : :(In fact, I'd say in general that any operation ought to be classwide if :it isn't explicitly a primitive. I think you are right. That's effectively what happens in Eiffel. : IMHO, only under very rare :circumstances should a non-primitive operation restrict a parameter to :accept only a specific root type but not any of its derived types.) : :Don Harrison, <donh@syd.csa.com.au> responded to Norman Cohen's example: : :>This looks like the approach that was suggested by Dale Stanborough. :>I think he found, however that Op1 and Op2 in the child packages were no longer :>inheritable. : :And why should they _need_ to be "inheritable"? A subprogram does not :need to be a _primitive_ in order to be _reusable_. Indeed, a :"classwide" subprogram is quintessentially reusable -- by definition, :it can be applied to any object of any type in its type-class, whether :that type be the root type or some derived type. They should be inheritable if the language is to offer a simple and consistent mechanism for inheritance/polymorphism. In Eiffel, there is only one species of type - the class - which may be viewed as specific or class-wide depending on the context. This makes modelling considerably easier. Ada complicates matters by forcing an atificial distinction of specific versus classwide depending on usage. Since the formal and actual parameters of an operation may be either specific or classwide, the number of possible combinations is 4 compared with 1 in Eiffel. To my mind, this is 4 times more complicated than is really needed. :IMHO, Ada95's clear :distinction between a specific root type (such as Abstraction_1.T) and :its associated classwide type (such as Abstraction_1.T'Class) makes :this kind of issue a lot easier to understand. Well, I have to disagree. I think it complicates it as argued above. :There's only one thing that you might find "awkward" about this: If you :want to invoke, say, Abstraction_1.Specialty_1.Op_1 on an Abstraction_2.T :object, you can do that, but you still have to name the operation :"Abstraction_1.Specialty_1.Op_1" (unless of course you use a use_clause). :You won't "automagically" get a visible operation called :"Abstraction_2.Op_1" that you can call. Yes, that is awkward. Of course, it would be much tidier to remove 'with' clauses altogether because they are redundant. Information about which modules are imported by another is implcit in the references/calls made by the client module. This is the approach taken in Eiffel. Naturally, the inherited modules must be specified; otherwise you would have to uniquely name every attribute and operation in the system! :But so what? Isn't the premise here that a child package like :Abstraction_1.Specialty_1 represents a special interface designed for :exclusive use by one client (or perhaps a few), such as Client_1? That :client is most likely to be interested in a particular abstraction at a :particular level of your class hierarchy. Presumably, all that Client_1 :cares about is that it's dealing with some object in the class :Abstraction_1.T'Class. Perhaps, but it is less flexible nonetheless. : The root type, Abstraction_1.T, might have :derived types beneath it in the hierarchy, but we're assuming that :Client_1 doesn't care what they are, so long as they all adhere to the :abstraction established for Abstraction_1.T'Class (i.e., Liskov :substitutability is upheld). What's Liskov substitutability? :If Client_1 were instead interested in a different abstraction at a :different level in the type hierarchy, then we simply wouldn't set up :Abstraction_1.Specialty_1 as the exclusive special interface it would :use. We might, perhaps, have Abstraction_2.Specialty_1 instead. : :Don Harrison, <donh@syd.csa.com.au> writes: : :>Now, what happens when we derive a new type from T? :> :>package New_Parent is :> type New_T is new T with ... :>private :> procedure New_Op1 (X: in out New_T); :> procedure New_Op2 (X: in out New_T); :>end New_Parent; :> :>package New_Parent.Child1 is :> procedure New_Op1 (X: in out New_T); :>end New_Parent.Child1; :> :>package body New_Parent.Child1 is :> procedure New_Op1 (X: in out New_T) renames New_Parent.New_Op1; :>end New_Parent.Child1; :> :>[Similarly, for New_Parent.Child2] :> :>We lose the export status of Op1 and Op2 because it was defined in the :>child packages. It's all very messy. : :It's not clear what you're trying to do here. Is the fact that you're :re-using the names "Child1" and "Child2" significant? Is there :supposed to be a connection between, for instance, Parent.Child1 and :New_Parent.Child1 -- in other words, do you intend them to relate to :the same "specialty"? Yes. Sorry for the confusion. New_Parent.Child1 is intended as a refinement of Parent.Child1. I should have used Op1 instead of New_Op1 and T instead of New_T, as you did in your example. :If that's what you mean, well, of course an Ada95 compiler won't read :any special significance into the similarity of child-names (any more :than a family-tree program would read anything into the fact that two :boys from two completely different families both happened to be named :"John"). Yes, that would be a big ask! : So if you have a client that's interested in a particular :"specialty", but several different types participate in this specialty, :then the client just have to "with" each of the various related child :packages. : :But what's so bad about that? It's bad for the following reasons: 1) Because you have to distribute the definition of the abstraction over multiple modules - quasi-encapsulation. In this case, with only 2 views of the abstraction, you need 6 modules. 1 would have sufficed (assuming a bona fide selective export mechanism and combined interface and implementation); 2 if you insist on separate interface and implementation. 2) The selective export information has to be repeated for descendants of the abstraction rather than being inherited. This goes against on of the fundamental tenets of OO - uniqueness. Do something once and once only. 3) It is indicative of a more basic flaw in the language - the 'everyone sees it or no-one sees it' model of information hiding. In other words, it is a hack (albeit a good one). :This seems to work just fine, especially :if you make the "specialty" operations classwide, as I suggest: : : : with Abstraction_1; : package Abstraction_4 is : type T is new Abstraction_1.T with null record; : private : procedure Op_3 (X: in out T); -- for Specialty_1 : procedure Op_4 (X: in out T); -- for Specialty_2 : end Abstraction_4; : : : package Abstraction_4.Specialty_1 is : procedure Op_3 (X: in out T'Class); -- classwide operation : end Abstraction_4.Specialty_1; : : package Abstraction_4.Specialty_1 is : procedure Op_3 (X: in out T'Class); : begin : Abstraction_4.Op_3 (T); -- dispatching call : end Op_3; : end Abstraction_4.Specialty_1; : : : with Abstraction_1.Specialty_1; : with Abstraction_4.Specialty_1; : procedure Client_1 is : Some_Abstraction_1 : Abstraction_1.T'Class := ... : Some_Abstraction_4 : Abstraction_4.T'Class := ... : begin : ... : Abstraction_1.Specialty_1.Op_1 (Some_Abstraction_1); : ... : Abstraction_4.Specialty_1.Op_3 (Some_Abstraction_4); : ... : Abstraction_1.Specialty_1.Op_1 : (Abstraction_1.T'Class (Some_Abstraction_4)); : -- widening : ... : if Some_Abstraction_1 in Abstraction_4.T'Class then : Abstraction_4.Specialty_1.Op_3 : (Abstraction_4.T'Class (Some_Abstraction_1)); : -- narrowing : end if; : ... : end Client_1; : : : ... similarly for Abstraction_4.Specialty_2.Op_4 and Client_2 ... A good workaround. :This seems methodologically quite sound to me. It clearly shows that :Client_1 depends on aspects of Specialty_1 that are manifested at :different levels of the type hierarchy. Operations related to :Specialty_1 that are appropriate for a wider class of objects are :clearly associated with the package that declares the wider type :(Abstraction_1); while operations that are appropriate only for a :narrower class of objects are clearly associated with the package that :declares the narrower type (Abstraction_4). : :What's so "messy" about that? A fair bit, but it's probably as good as you're going to acheive within the constraints placed on you. :(Well, this whole discussion might be a lot clearer if we had a more :concrete example that exhibited this kind of pattern. Ideas anyone?) Why be concrete when you can be vague? I certainly am ;-). :------------------------------------------------------------------------ :Internet.Usenet.Put_Signature :( Name => "John G. Volan", E_Mail => "John_Volan@dayton.saic.com", : Favorite_Slogan => "Ada95: The *FIRST* International-Standard OOPL", : Humorous_Disclaimer => "These opinions are undefined by SAIC, so" & : "any use would be erroneous ... or is that a bounded error now?" ); :------------------------------------------------------------------------ Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-22 0:00 ` Don Harrison @ 1996-03-22 0:00 ` Norman H. Cohen 1996-03-26 0:00 ` Don Harrison 1996-03-22 0:00 ` Norman H. Cohen ` (2 subsequent siblings) 3 siblings, 1 reply; 218+ messages in thread From: Norman H. Cohen @ 1996-03-22 0:00 UTC (permalink / raw) When Don Harrison asked how to achieve selective export in Ada, I proposed a technique, but I never asked why one would want selective export. Selective export does indeed help document the modular structure of a program, and make the program easier for a reader to understand, but it seems to me that the documentation is in the wrong place. In general, the writer of a program component should be as unaware as possible of the context in which that component will be used, especially if loose coupling and reusability are valued. I don't find Bertrand Meyer's example of selective export in _Object-Oriented_Software_Construction_ particularly compelling. That example would be written in Ada as follows: generic type Element_Type is private; package Lists is type List_Type is private; ... private type Cell_Type; type Cell_Pointer_Type is access Cell_Type; type Cell_Type is record Data : Element_Type; Link : Cell_Pointer_Type; end record; type List_Type is record Length : Natural; Cells : Cell_Pointer_Type; end record; end Lists; However, because of the Eiffel constraint that module = class = type, Meyer cannot define List_Type in the same class as Cell_Type. Therefore, he puts his equivalent of Cell_Type in a separate class which is selectively exported only to the List_Type class. (There is no need for an explicit Cell_Pointer_Type in Eiffel because of Eiffel's implicit reference semantics; the Link component belongs to Cell_Type.) Perhaps Don will decry this as "quasi-encapsulation", but the whole point of the selective export in Eiffel was to emphasize that the Eiffel class corresponding to Cell_Pointer_Type was to be used only in the module defining the Eiffel analog of List_Type. Nesting multiple type definitions (suitably hidden) inside a single Ada package accomplishes the same thing. Because of the ability to define more than one type in the same module in Ada, selective export is of less interest to an Ada programmer than to an Eiffel programmer. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-22 0:00 ` Norman H. Cohen @ 1996-03-26 0:00 ` Don Harrison 1996-03-26 0:00 ` Norman H. Cohen 1996-03-27 0:00 ` Thomas Beale 0 siblings, 2 replies; 218+ messages in thread From: Don Harrison @ 1996-03-26 0:00 UTC (permalink / raw) Norman H. Cohen wrote: :When Don Harrison asked how to achieve selective export in Ada, I :proposed a technique, but I never asked why one would want selective :export. : :Selective export does indeed help document the modular structure of a :program, and make the program easier for a reader to understand, but it :seems to me that the documentation is in the wrong place. In general, :the writer of a program component should be as unaware as possible of the :context in which that component will be used, especially if loose :coupling and reusability are valued. The Eiffel selective export may seem the wrong way of doing it at first sight. You might wonder: "What's the supplier class doing dictating who can and can't use it's features? It exists for the purpose of it's clients, so, if anything, named subsets of exported features should be provided by a supplier and clients should be able to choose which subset they need". However, this would be a violation of Design by Contract because it is the supplier class that must control it's own state. Therefore, it must dictate how it's services are used and by whom. If the client were able to able to choose a particular interface, it might be able to choose one which was more permissive than the supplier had intended for it and the door would be open to the client potentially changing the supplier to an inconsistent state. The intention of selective export is for classes to make available to 'closely related' classes features that are intended specifically for them. When the features are selectively exported to the same class, it means they are available to other instances of the same class and when they are exported to NONE, they are 'secret', like attributes and operations in an Ada package body. When features are not specifically designed for a particular class, they should be generally exported (by omitting the export clause) which, as you would expect, is the default. This acheives the loose coupling and reusability which you correctly identify should be the norm. :I don't find Bertrand Meyer's example of selective export in :_Object-Oriented_Software_Construction_ particularly compelling. That :example would be written in Ada as follows: : : generic : type Element_Type is private; : package Lists is : type List_Type is private; : ... : private : type Cell_Type; : type Cell_Pointer_Type is access Cell_Type; : type Cell_Type is : record : Data : Element_Type; : Link : Cell_Pointer_Type; : end record; : type List_Type is : record : Length : Natural; : Cells : Cell_Pointer_Type; : end record; : end Lists; : :However, because of the Eiffel constraint that module = class = type, :Meyer cannot define List_Type in the same class as Cell_Type. Nor does he wish to :-). : Therefore, :he puts his equivalent of Cell_Type in a separate class which is :selectively exported only to the List_Type class. a la: class CELL [ELEMENT_TYPE] feature {LIST} data : ELEMENT_TYPE link : CELL ... end class LIST [ELEMENT_TYPE] feature length : INTEGER cells : CELL ... end : (There is no need for :an explicit Cell_Pointer_Type in Eiffel because of Eiffel's implicit :reference semantics; the Link component belongs to Cell_Type.) Great idea! :Perhaps Don will decry this as "quasi-encapsulation", but the whole point :of the selective export in Eiffel was to emphasize that the Eiffel class :corresponding to Cell_Pointer_Type was to be used only in the module :defining the Eiffel analog of List_Type. No, I wouldn't because they are distinct abstractions (albeit closely related ones). : Nesting multiple type :definitions (suitably hidden) inside a single Ada package accomplishes :the same thing. By no means! The co-encapsulated model destroys the export contract between the related abstractions. What you end up with is a free-for-all for whoever happens to live in the same module. "You want to change my state? Well, step right up and do whatever you like!". Likewise, you are forced to provide a one-size-fits-all export contract to clients outside the module, which, as argued above, can also result in the abstractions being changed to inconsistent states. Basically, export contracts get shot to pieces or don't exist at all (internally) if co-encapsulation is used. The alternative is the not-so-beautiful quasi-encasulated paradigm already discussed. The Eiffel approach allow more control, more elegantly, IMO. :Because of the ability to define more than one type in the same module in :Ada, selective export is of less interest to an Ada programmer than to an :Eiffel programmer. It ought to be, IMO. I suppose, if contracting were treated with the same importance in Ada as it is in Eiffel, they would be just as interested :-). :-- :Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-26 0:00 ` Don Harrison @ 1996-03-26 0:00 ` Norman H. Cohen 1996-03-29 0:00 ` Don Harrison 1996-03-27 0:00 ` Thomas Beale 1 sibling, 1 reply; 218+ messages in thread From: Norman H. Cohen @ 1996-03-26 0:00 UTC (permalink / raw) In article <Dov7D0.AtM@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: |> The Eiffel selective export may seem the wrong way of doing it at first sight. |> You might wonder: |> |> "What's the supplier class doing dictating who can and can't use it's |> features? It exists for the purpose of it's clients, so, if anything, |> named subsets of exported features should be provided by a supplier |> and clients should be able to choose which subset they need". Indeed, I do. |> However, this would be a violation of Design by Contract because it is the |> supplier class that must control it's own state. Therefore, it must dictate |> how it's services are used and by whom. If the client were able to able to |> choose a particular interface, it might be able to choose one which was more |> permissive than the supplier had intended for it and the door would be open |> to the client potentially changing the supplier to an inconsistent state. Selective export cannot control HOW a supplier's services are used, only by whom. Don is suggesting that some modules can be better trusted than others to use certain services correctly. But such modules should not be called clients, they should be called partners. Indeed, Don continues: |> The intention of selective export is for classes to make available to 'closely |> related' classes features that are intended specifically for them. Now what do we mean by "closely related"? In part, we mean that one of the classes is implemented in terms of internal features of the other (internal at least in the sense that such features are not available to the general public). The classes were probably designed in conjunction with each other, most likely by the same group of designers, as part of a cooperative system. By giving its partner access to privileged operations, a class is expressing trust in that partner. ... |> : Nesting multiple type |> :definitions (suitably hidden) inside a single Ada package accomplishes |> :the same thing. |> |> By no means! The co-encapsulated model destroys the export contract between the |> related abstractions. What you end up with is a free-for-all for whoever |> happens to live in the same module. "You want to change my state? Well, step |> right up and do whatever you like!". This is a distinction without a difference. Only parts of the program that trust each other live in the same module. In Eiffel, the definer of type A expresses trust in the definer of type B by selectively exporting to B the right to use potentially destructive features. If B wants to change A's state, it can "step right up" and do so. In Ada this trust can be expressed by co-encapsulation--declaring A and B in the same package. The issue of scale is important here. The example in question involved one type for linked list cells and another type for abstract lists, consisting of a length and (a reference to) the first linked cell. The whole package is a few dozen lines, conceived as a single entity. This is not a toy example. Along with the complex data abstractions found in real-world programs are lots and lots of very simple data abstractions like this one, and they account for a considerable portion of program text. For multitype data abstractions like this one, artificial barriers between the two types just get in the way. Ada allows the barriers to be dispensed with at the programmer's discretion; Eiffel does not. Ada also allows more intricate program structures, involving private child packages, that allow parts of a package's implementation to be separately encapsulated and to provide features that are hidden from outside clients; however such structures are not forced upon the programmer in cases where they are overkill. ... |> :Because of the ability to define more than one type in the same module in |> :Ada, selective export is of less interest to an Ada programmer than to an |> :Eiffel programmer. |> |> It ought to be, IMO. I suppose, if contracting were treated with the same |> importance in Ada as it is in Eiffel, they would be just as interested :-). Actually, Ada is all about contracts, where they are appropriate. I am reminded of a talk Lou Gerstner gave shortly after he became CEO of IBM, in which he expressed his astonishment at the formal legal contracts that he found being drawn up between different divisions of the same corporation. This did not diminish in any way his appreciation of the importance of contracts with outside parties. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-26 0:00 ` Norman H. Cohen @ 1996-03-29 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-03-29 0:00 UTC (permalink / raw) Norman wrote: :In article <Dov7D0.AtM@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) :writes: : :|> The Eiffel selective export may seem the wrong way of doing it at first sight. :|> You might wonder: :|> :|> "What's the supplier class doing dictating who can and can't use it's :|> features? It exists for the purpose of it's clients, so, if anything, :|> named subsets of exported features should be provided by a supplier :|> and clients should be able to choose which subset they need". : :Indeed, I do. That's okay. I'm a slow learner too :-). : :|> However, this would be a violation of Design by Contract because it is the :|> supplier class that must control it's own state. Therefore, it must dictate :|> how it's services are used and by whom. If the client were able to able to :|> choose a particular interface, it might be able to choose one which was more :|> permissive than the supplier had intended for it and the door would be open :|> to the client potentially changing the supplier to an inconsistent state. : :Selective export cannot control HOW a supplier's services are used, only :by whom. The HOW is specified in Eiffel in two ways: i) Selective export not only says WHO but WHAT (attributes/operations). The combination of the two reflects the privileges offered by an abstraction to it's partner/client. ii) Executable preconditions. (My guess is that these were omitted from Ada 95 because it would be difficult or messy to control the state of individual abstractions because they are co-encapsulated (It's hard enough finding them, let alone controlling them!). The other alternative would be to assert at the package level but that would be a complete joke). Example: class A feature -- anyone may use f1 f1 feature {B, C} -- Only instances of partners B and C may use f2 f2 feature {A} -- All instances of A may use f3 (including Current) f3 feature {NONE} -- Only the current instance of A may use f4 (secret) f4. end :Don is suggesting that some modules can be better trusted than :others to use certain services correctly. Only because they are designed to co-operate with each other. It would be inaccurate to think of this protection only from the perspective of the supplier. For the client, selective export gives them the reassurance that they can't inadvertently misuse the supplier's features. There is no such reassurance for co-encapsulated abstractions. (I wish someone would give me a dollar for every time I had to fix a bug caused by one component of an Ada package misusing another!) :But such modules should not be :called clients, they should be called partners. They are partners and also clients. That relationship may be asymmetical in cases (eg. the CELL/LIST example) in which CELL exports to LIST and not vice versa. : Indeed, Don continues: : :|> The intention of selective export is for classes to make available to 'closely :|> related' classes features that are intended specifically for them. : :Now what do we mean by "closely related"? In part, we mean that one of :the classes is implemented in terms of internal features of the other :(internal at least in the sense that such features are not available to :the general public). The classes were probably designed in conjunction :with each other, most likely by the same group of designers, as part of a :cooperative system. By giving its partner access to privileged :operations, a class is expressing trust in that partner. Expressing trust, yes, but in specific ways so that the privileges are not carte blanche. :.... :|> : Nesting multiple type :|> :definitions (suitably hidden) inside a single Ada package accomplishes :|> :the same thing. :|> :|> By no means! The co-encapsulated model destroys the export contract between the :|> related abstractions. What you end up with is a free-for-all for whoever :|> happens to live in the same module. "You want to change my state? Well, step :|> right up and do whatever you like!". : :This is a distinction without a difference. There is an enormous difference. : Only parts of the program :that trust each other live in the same module. In Eiffel, the definer :of type A expresses trust in the definer of type B by selectively :exporting to B the right to use potentially destructive features. If B :wants to change A's state, it can "step right up" and do so. In Ada this :trust can be expressed by co-encapsulation--declaring A and B in the same :package. That trust is misguided because it is unrestricted. :The issue of scale is important here. IMO, OO principles are equally relevant at all levels of abstraction. Why? Because the essence of OO is (contolled) reuse. : The example in question involved :one type for linked list cells and another type for abstract lists, :consisting of a length and (a reference to) the first linked cell. :The whole package is a few dozen lines, conceived as a single entity. :This is not a toy example. I don't think anyone said it was. : Along with the complex data abstractions :found in real-world programs are lots and lots of very simple data :abstractions like this one, and they account for a considerable portion :of program text. For multitype data abstractions like this one, :artificial barriers between the two types just get in the way. The boundary between the partner abstractions is not a barrier and acknowledging it's existence only serves to enhance reliability and reusability. : Ada :allows the barriers to be dispensed with at the programmer's discretion; You sound like a C programmer. :Eiffel does not. Ada also allows more intricate program structures, :involving private child packages, that allow parts of a package's :implementation to be separately encapsulated and to provide :features that are hidden from outside clients; however such structures :are not forced upon the programmer in cases where they are overkill. Well, gee. And I thought complexity was a bad thing :-). :.... :|> :Because of the ability to define more than one type in the same module in :|> :Ada, selective export is of less interest to an Ada programmer than to an :|> :Eiffel programmer. :|> :|> It ought to be, IMO. I suppose, if contracting were treated with the same :|> importance in Ada as it is in Eiffel, they would be just as interested :-). : :Actually, Ada is all about contracts, where they are appropriate. I am :reminded of a talk Lou Gerstner gave shortly after he became CEO of IBM, :in which he expressed his astonishment at the formal legal contracts that :he found being drawn up between different divisions of the same :corporation. This did not diminish in any way his appreciation of the :importance of contracts with outside parties. I hope no-one would disagree that formal legal contracts between divisions of the same corporation are indicative of an apalling corporate culture and that a co-operative relationship between divisions is a good thing. However, it is important to realise that the key to effective operation is a suitable balance between access to each other's resources and the discipline of how those resources are made available. A manager of one division cannot just march into another and relegate staff from that division without first getting the approval of that division's manager. Otherwise, the organisation would be hopelessly inefficient and collapse due to anarchy. (Perhaps this has some bearing on IBM's fall from grace :-). However, there are promising signs that it is improving). No, control is still needed, but only as much as required. :-- :Norman H. Cohen ncohen@watson.ibm.com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-26 0:00 ` Don Harrison 1996-03-26 0:00 ` Norman H. Cohen @ 1996-03-27 0:00 ` Thomas Beale 1996-03-28 0:00 ` Don Harrison 1 sibling, 1 reply; 218+ messages in thread From: Thomas Beale @ 1996-03-27 0:00 UTC (permalink / raw) In article <Dov7D0.AtM@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: >Norman H. Cohen wrote: > >:When Don Harrison asked how to achieve selective export in Ada, I >:proposed a technique, but I never asked why one would want selective >:export. >: >:Selective export does indeed help document the modular structure of a >:program, and make the program easier for a reader to understand, but it >:seems to me that the documentation is in the wrong place. In general, >:the writer of a program component should be as unaware as possible of the >:context in which that component will be used, especially if loose >:coupling and reusability are valued. > This area may seem initially to be one where Eiffel takes the wrong tack. But consider two things: a) we shouldn't be obsessed with single-class encapsulation at the expense of allowing coarse-grained objects consisting of collaborating fine-grained instances. Such coarse-grained entities represent closely co-operating and "co-designed" abstractions; for example most design patterns and small software components would fall into this category. I would suggest that the really important boundary of re-use is not around the single class, but around small groups of classes designed to realise a coarser abstraction. b) the Eiffel export mechanism can be used to specify an abstract parent class e.g. feature {LIST} This effectively says that any class conforming to the design intention of LIST may have access to the following features. Thus selective export does not restrict the client to be a single effective class, but to classes conforming to a design idea. - Thomas Beale ____________________________________________________________________________ Thomas Beale | Class Technology | Email: thomas@class.com.au PO Box 6274 North Sydney | NSW 2060 | Ph: +61 2 9922 7222 AUSTRALIA | Fax: +61 2 9922 7703 _______________________________________|____________________________________ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-27 0:00 ` Thomas Beale @ 1996-03-28 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-03-28 0:00 UTC (permalink / raw) Thomas, Hi! You wrote: :In article <Dov7D0.AtM@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: :>Norman H. Cohen wrote: :> :>:When Don Harrison asked how to achieve selective export in Ada, I :>:proposed a technique, but I never asked why one would want selective :>:export. :>: :>:Selective export does indeed help document the modular structure of a :>:program, and make the program easier for a reader to understand, but it :>:seems to me that the documentation is in the wrong place. In general, :>:the writer of a program component should be as unaware as possible of the :>:context in which that component will be used, especially if loose :>:coupling and reusability are valued. :> : :This area may seem initially to be one where Eiffel takes the wrong :tack. But consider two things: : :a) we shouldn't be obsessed with single-class encapsulation at the : expense of allowing coarse-grained objects consisting of : collaborating fine-grained instances. Such coarse-grained entities : represent closely co-operating and "co-designed" abstractions; for : example most design patterns and small software components would : fall into this category. I would suggest that the really important : boundary of re-use is not around the single class, but around small : groups of classes designed to realise a coarser abstraction. Wouldn't structuring depend on whether or not the behaviour is self contained or not? Consider each case: 1) Self contained - we would then want to wrap the co-operating abstractions into a higher level abstraction, perhaps using a deferred class with constrained generic parameters. Then the design would offer a single cohesive interface for easy reuse. 2) Not self contained - the co-operating abstractions are linked to other abstractions so may not be encapsulated. Would this indicate a design flaw? It would certainly make reuse more difficult. Which one corresponds to design patterns? Both? :b) the Eiffel export mechanism can be used to specify an abstract : parent class e.g. : : feature {LIST} : : This effectively says that any class conforming to the design : intention of LIST may have access to the following features. Thus : selective export does not restrict the client to be a single : effective class, but to classes conforming to a design idea. So, LIST would be 'deferred' in this case? [Eiffel analogue of 'abstract']. :- Thomas Beale : :____________________________________________________________________________ :Thomas Beale | : Class Technology | Email: thomas@class.com.au : PO Box 6274 North Sydney | : NSW 2060 | Ph: +61 2 9922 7222 : AUSTRALIA | Fax: +61 2 9922 7703 :_______________________________________|____________________________________ Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-22 0:00 ` Don Harrison 1996-03-22 0:00 ` Norman H. Cohen @ 1996-03-22 0:00 ` Norman H. Cohen 1996-03-27 0:00 ` Don Harrison 1996-03-23 0:00 ` Joachim Durchholz 1996-03-28 0:00 ` Real OO Joachim Durchholz 3 siblings, 1 reply; 218+ messages in thread From: Norman H. Cohen @ 1996-03-22 0:00 UTC (permalink / raw) In article <DonJqn.755@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: |> In Eiffel, there is only one species of |> type - the class - which may be viewed as specific or class-wide depending on |> the context. This makes modelling considerably easier. |> |> Ada complicates matters by forcing an atificial distinction of specific versus |> classwide depending on usage. Since the formal and actual parameters of an |> operation may be either specific or classwide, the number of possible |> combinations is 4 compared with 1 in Eiffel. To my mind, this is 4 times more |> complicated than is really needed. This is not an artificial distinction. Let us consider actual parameters and formal parameters separately. In the case of actual parameters, it is a formalization of the distinction that Bertrand Meyer recognizes between the static type and the dynamic type of a reference. Think of an Ada actual parameter of a specific type as an Eiffel reference whose dynamic type is known to be identical to its static type. A method call can be far more efficient if this knowledge is conveyed to the compiler, and it is common in practice for this knowledge to be available. (The extra efficiency arises not only from saving a cycle or two for an indirect jump, but also because of the possibility of inlining the method body or performing precise interprocedural analysis.) In the case of formal parameters, the use of a classwide type or specific type reflects the distinction between a single type and the hierarchy of types descended (in zero or more steps) from that type. (In Eiffel, the word "class" refers to any one of these types; in Ada, the word "class" refers to the hierarchy, so "classwide" means "hierarchy-wide".) A classwide subprogram (i.e., one with classwide formal parameters) is one whose algorithm is the same for all objects in the hierarchy. A dispatching subprogram (i.e., one with specific formal parameters) is one whose algorithm depends on the tag (in Eiffel terms, the dynamic type) of the actual parameter. The body of a classwide subprogram relies only on properties common to all types in the hierarchy, i.e., features introduced at the root of the hierarchy. These may include record components, other classwide subprograms, and dispatching operations. For a one-parameter subprogram, the effect of a classwide subprogram could be achieved by a dispatching subprogram that is never overridden. Making it classwide simply documents the fact that the algorithm does not depend on the tag of the parameter, and causes the compiler to catch any attempt to override. The real power of a classwide program arises with multiple parameters. In Eiffel, dispatching is controlled by the one object whose method is invoked (the x in x.f()). In an Ada dispatching subprogram, there can be multiple parameters controlling the dispatching. Consider a hierarchy for geometric figures, with Shape_Type at the root and types such as Circle_Type, Triangle_Type, and Rectangle_Type at the leaves. There may be a dispatching operation Corresponding_Parts_Equal defined (as an abstract function) for Shape_Type and overridden for each shape. For example: function Corresponding_Parts_Equal (C1, C2: Circle_Type) return Boolean is begin return C1.Radius = C2.Radius; end Corresponding_Parts_Equal; function Corresponding_Parts_Equal (R1, R2: Rectangle_Type) return Boolean is begin return (R1.Base = R2.Base and R1.Height = R2.Height) or (R1.Base = R2.Height and R1.Height = R2.Base); end Corresponding_Parts_Equal; Each time a new kind of shape is introduced to the hierarchy, a new overriding version of Corresponding_Parts_Equal, concerned only with the features of that kind of shape, is written. The call Corresponding_Parts_Equal(Shape1, Shape2), where Shape1 and Shape2 are of type Shape_Type'Class (in Eiffel terms, their static types are Shape_Type and their dynamic types are unknown), will dispatch to the first body above if the dynamic types of Shape1 and Shape2 are both Circle_Type, to the second body above if the dynamic types are both Rectangle_Type, and so forth. But if Shape1 and Shape2 have different dynamic tags, there is no sensible way to check that their corresponding parts are equal, and a run-time error results. That leaves the problem of how to determine whether two arbitrary Shape_Type'Class values, possibly with different tags, are congruent, and this is where classwide subprograms come in. There is no requirement that the parameters of a classwide subprogram have the same tag. Therefore, we can write: function Congruent (Shape1, Shape2: Shape_Type'Class) return Boolean is begin return Shape1'Tag = Shape2'Tag and then Corresponding_Parts_Equal (Shape1, Shape2); end Congruent; When the shapes have different tags, the function immediately returns False without invoking Corresponding_Parts_Equal. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-22 0:00 ` Norman H. Cohen @ 1996-03-27 0:00 ` Don Harrison 1996-03-27 0:00 ` Norman H. Cohen 0 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-03-27 0:00 UTC (permalink / raw) Norman H. Cohen wrote: :In article <DonJqn.755@assip.csasyd.oz>, donh@syd.csa.com.au :(Don Harrison) writes: : :|> In Eiffel, there is only one species of :|> type - the class - which may be viewed as specific or class-wide depending on :|> the context. This makes modelling considerably easier. :|> :|> Ada complicates matters by forcing an atificial distinction of specific versus :|> classwide depending on usage. Since the formal and actual parameters of an :|> operation may be either specific or classwide, the number of possible :|> combinations is 4 compared with 1 in Eiffel. To my mind, this is 4 times more :|> complicated than is really needed. : :This is not an artificial distinction. Let us consider actual parameters :and formal parameters separately. I note that you spell better than I do :-). Whether we call it 'artificial' or not doesn't matter too much, I guess. What is significant, is whether it is necessary to make such a distinction. There are a number of issues: a) Is there a significant difference in efficiency between the two approaches? Quantitatively, what is the relative difference in efficiency of Jump to Subroutine compared with an Indirect Jump to Subroutine? As Joachim pointed out, Eiffel compilers typically optimise where possible. Perhaps optimisation by the developer would be more effective but remember that efficiency is being traded off against the power and flexibility of dynamic binding. b) What is more important for the job at hand - flexibility and reusability or efficiency? As Joachim indicated, Eiffel developers would be more concerned with flexibility and reusability, prefering to maximise dynamic binding rather than squeezing every last clock cycle out of the processor. Admittedly, however, an Ada developer implementing a real-time system with stringent timing constraints may be more interested in efficiency. c) How do the mechanisms compare wrt clarity of expression? IMO, the Eiffel mechanism simplifies reasoning. :In the case of actual parameters, it is a formalization of the :distinction that Bertrand Meyer recognizes between the static type and :the dynamic type of a reference. Think of an Ada actual parameter of a :specific type as an Eiffel reference whose dynamic type is known to be :identical to its static type. A method call can be far more efficient if :this knowledge is conveyed to the compiler, and it is common in practice :for this knowledge to be available. (The extra efficiency arises not :only from saving a cycle or two for an indirect jump, but also because of :the possibility of inlining the method body or performing precise :interprocedural analysis.) I guess the difference in performance here would depend on how effectively Eiffel compilers optimise (by staticly binding and inlining) and how much slower an indirect jump is. :In the case of formal parameters, the use of a classwide type or specific :type reflects the distinction between a single type and the hierarchy of :types descended (in zero or more steps) from that type. (In Eiffel, the :word "class" refers to any one of these types; in Ada, the word "class" :refers to the hierarchy, so "classwide" means "hierarchy-wide".) A :classwide subprogram (i.e., one with classwide formal parameters) is one :whose algorithm is the same for all objects in the hierarchy. A :dispatching subprogram (i.e., one with specific formal parameters) is one :whose algorithm depends on the tag (in Eiffel terms, the dynamic type) of :the actual parameter. The body of a classwide subprogram relies only on :properties common to all types in the hierarchy, i.e., features :introduced at the root of the hierarchy. These may include record :components, other classwide subprograms, and dispatching operations. An Eiffel operation may effectively be made 'classwide' by declaring it 'frozen'. The real purpose of 'frozen' is to prevent the implementation being changed in descendants so that they may rely on fixed universal semantics. Freezing features for efficiency purposes is a hack as pointed out by Bob Duff. Note that you generally would not want to freeze an implementation because flexibility is reduced. (You can both freeze and provide an overridable version by using two copies but that's another matter). :For a one-parameter subprogram, the effect of a classwide subprogram :could be achieved by a dispatching subprogram that is never overridden. :Making it classwide simply documents the fact that the algorithm does not :depend on the tag of the parameter, and causes the compiler to catch any :attempt to override. The real power of a classwide program arises with :multiple parameters. In Eiffel, dispatching is controlled by the one :object whose method is invoked (the x in x.f()). In an Ada dispatching :subprogram, there can be multiple parameters controlling the dispatching. :Consider a hierarchy for geometric figures, with Shape_Type at the root :and types such as Circle_Type, Triangle_Type, and Rectangle_Type at the :leaves. They may all be regarded as controlling dispatching only because they happen to be of the same type. It is really the common type of the operands that is controlling dispatching rather than the operands per se. The operation being executed belongs to the class, not the instances of the class - the same as Eiffel. I prefer the dot notation of the Eiffel syntax because all the parameters in the parentheses have the same status - they are all effectively classwide - and the controlling operand is clearly identified because it the target of the call. There is no need to put up a signpost to say which is classwide and which is not as is necessary with co-encapsulation. The information is implicit. In the case of 'frozen', that information is explicit but conveys different semantics. : There may be a dispatching operation Corresponding_Parts_Equal :defined (as an abstract function) for Shape_Type and overridden for each :shape. For example: : : function Corresponding_Parts_Equal (C1, C2: Circle_Type) return Boolean is : begin : return C1.Radius = C2.Radius; : end Corresponding_Parts_Equal; : : : function Corresponding_Parts_Equal : (R1, R2: Rectangle_Type) return Boolean is : begin : return : (R1.Base = R2.Base and R1.Height = R2.Height) or : (R1.Base = R2.Height and R1.Height = R2.Base); : end Corresponding_Parts_Equal; : :Each time a new kind of shape is introduced to the hierarchy, a new :overriding version of Corresponding_Parts_Equal, concerned only with the :features of that kind of shape, is written. : :The call Corresponding_Parts_Equal(Shape1, Shape2), where Shape1 and :Shape2 are of type Shape_Type'Class (in Eiffel terms, their static types :are Shape_Type and their dynamic types are unknown), will dispatch to the :first body above if the dynamic types of Shape1 and Shape2 are both :Circle_Type, to the second body above if the dynamic types are both :Rectangle_Type, and so forth. But if Shape1 and Shape2 have different :dynamic tags, there is no sensible way to check that their corresponding :parts are equal, and a run-time error results. That leaves the problem :of how to determine whether two arbitrary Shape_Type'Class values, :possibly with different tags, are congruent, and this is where classwide :subprograms come in. There is no requirement that the parameters of a :classwide subprogram have the same tag. Therefore, we can write: : : function Congruent (Shape1, Shape2: Shape_Type'Class) return Boolean is : begin : return : Shape1'Tag = Shape2'Tag and then : Corresponding_Parts_Equal (Shape1, Shape2); : end Congruent; : :When the shapes have different tags, the function immediately returns :False without invoking Corresponding_Parts_Equal. This might be done in Eiffel by: class CONGRUENCY inherit INTERNAL feature congruent (a, b: SHAPE): BOOLEAN is do Result := (dynamic_type (a) = dynamic_type (b)) and then equal (a, b) end end Yes, short circuit booleans were stolen from Ada. Thanks, Ada :-). Note that equal may be protected with a precondition thus: equal (a, b: ANY): BOOLEAN is require same_type: dynamic_type (a) = dynamic_type (b) deferred end (Not sure of the details, but you see what I mean). :-- :Norman H. Cohen ncohen@watson.ibm.com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-27 0:00 ` Don Harrison @ 1996-03-27 0:00 ` Norman H. Cohen 1996-03-28 0:00 ` Jacob Gore 1996-04-04 0:00 ` Don Harrison 0 siblings, 2 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-03-27 0:00 UTC (permalink / raw) In article <Dox4J9.H6I@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: |> Quantitatively, what is the relative difference in efficiency of Jump to |> Subroutine compared with an Indirect Jump to Subroutine? In terms of instruction path length, just another one or two instructions per call. This by itself can be significant in a critical loop, but there are other costs as well. On a RISC machine, the use of an indirect branch instead of a direct one can disable branch prediction, resulting in I-cache misses that cause the machine to stall for the equivalent of several dozen instructions. In addition, the dynamic nature of the branch prevents inlining and interprocedural optimizations that depend on knowing which subprogram is called. |> As Joachim pointed out, Eiffel compilers typically optimise where possible. And as I pointed out, "where possible" tends to mean "in very few places" in practice, at least in the absence of profile-directed feedback. |> Perhaps optimisation by the developer would be more effective but remember |> that efficiency is being traded off against the power and flexibility of |> dynamic binding. No flexibility is being traded off. Your phrase "optimzation by the developer" conjures up images of programs made inflexible and inscrutable to improve efficiency, but that is not required with the Ada mechanism. All that is required is that, in a context where Rect1 and Rect2 are known to both have dynamic type Rectangle_Type, Rect1 and Rect2 should be declared to be of type Rectangle_Type rather than Shape_Type'Class. Then the call Corresponding_Parts_Equal(Rect1, Rect2) is resolved statically rather than dynamically. If anything, this increases the understandability of the code. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-27 0:00 ` Norman H. Cohen @ 1996-03-28 0:00 ` Jacob Gore 1996-04-04 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Jacob Gore @ 1996-03-28 0:00 UTC (permalink / raw) Norman H. Cohen writes > No flexibility is being traded off. Your phrase "optimzation by the > developer" conjures up images of programs made inflexible and inscrutable > to improve efficiency, but that is not required with the Ada mechanism. Inflexible and insrcutable are not necessarily related. > All that is required is that, in a context where Rect1 and Rect2 are > known to both have dynamic type Rectangle_Type, Rect1 and Rect2 should be > declared to be of type Rectangle_Type rather than Shape_Type'Class. Then > the call Corresponding_Parts_Equal(Rect1, Rect2) is resolved statically > rather than dynamically. If anything, this increases the > understandability of the code. The question is not whether it is better to use Rectangle_Type instead of Shape_Type'Class, but whether it is better to use Rectangle_Type'Class instead of Rectangle_Type. Static resolution makes it impossible to safely attach a more specific rectangle object to Rect1 or Rect2, so you do sacrifice flexibility. Why decide in advance that your code, when inherited by a subclass, will ignore that subclass' redefinitions of some (or all) of the routines? It is hard enough to write classes that can be easily refined through inheritance (which ability is one of O-O's most advertized selling points) with dynamic binding; static binding makes it that much harder. Jacob --- Jacob Gore, Eastern NM U. Jacob.Gore@ENMU.Edu | Jacob@ToolCASE.Com [comp.lang.eiffel readers will have seen this message before; sorry about that -- Jacob] ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-27 0:00 ` Norman H. Cohen 1996-03-28 0:00 ` Jacob Gore @ 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Tucker Taft ` (3 more replies) 1 sibling, 4 replies; 218+ messages in thread From: Don Harrison @ 1996-04-04 0:00 UTC (permalink / raw) In case anyone is confused about how Eiffel's and Ada's parameter mechanisms compare, the following hopefully summarizes them: SUPPLIER CLIENT BINDING -------- ------ ------- Assuming: Ada: type A_TYPE is private -- tagged m: A_TYPE; type B_TYPE is private -- tagged mc: A_TYPE'Class; n: B_TYPE; nc: B_TYPE'Class; Eiffel: class A_TYPE ... end m: A_TYPE class B_TYPE ... end n: B_TYPE Case1 ----- Ada: procedure f (a: A_TYPE) f (m); static f (mc); dynamic But if f not defined in same spec: f (m); static f (mc); static Eiffel (inside A_TYPE): f m.f dynamic Case 2 ------ Ada: procedure g (a: A_TYPE'Class) g (m) static g (mc) static Eiffel (inside A_TYPE): frozen g m.g static Case 3 ------ Ada: procedure h (a: A_TYPE; b: B_TYPE'Class) h (m, n) static h (m, nc) static h (mc, n) dynamic (on mc) h (mc, nc) dynamic (on mc) Eiffel (inside A_TYPE): h (b: B_TYPE) m.h (n) dynamic Case 4 ------ Ada: procedure i (a: A_TYPE'Class; b: B_TYPE'Class) i (m, n) static i (m, nc) static i (mc, n) static i (mc, nc) static Eiffel (inside A_TYPE): frozen i (b: B_TYPE) m.i (n) static ------------------------------------------------------------------------------------ The following is ILLEGAL because polymorphism requires that an operation can be primitive for at most one class. Case 5 ------ Ada: procedure j (a: A_TYPE; b: B_TYPE) Eiffel: No equivalent. ------------------------------------------------------------------------------------ Conclusion: Ada: - more efficient, but - manual optimisation places a burden on developer - reduced flexibility/reusability - syntactically complex Eiffel: - less efficient, but - automatic optimisation means that the the efficiency gap is reduced as is the burden on developer - greater flexibility/reusability - syntactically simple Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison @ 1996-04-04 0:00 ` Tucker Taft 1996-04-04 0:00 ` Tucker Taft 1996-04-12 0:00 ` Don Harrison 1996-04-04 0:00 ` Jon S Anthony ` (2 subsequent siblings) 3 siblings, 2 replies; 218+ messages in thread From: Tucker Taft @ 1996-04-04 0:00 UTC (permalink / raw) One correction, and one comment... Don Harrison (donh@syd.csa.com.au) wrote: : In case anyone is confused about how Eiffel's and Ada's parameter mechanisms : compare, the following hopefully summarizes them: : SUPPLIER CLIENT BINDING : -------- ------ ------- : Assuming: : Ada: : type A_TYPE is private -- tagged m: A_TYPE; : type B_TYPE is private -- tagged mc: A_TYPE'Class; : n: B_TYPE; : nc: B_TYPE'Class; : Eiffel: : class A_TYPE ... end m: A_TYPE : class B_TYPE ... end n: B_TYPE : Case1 : ----- : Ada: : procedure f (a: A_TYPE) f (m); static : f (mc); dynamic : But if f not defined in same spec: : f (m); static : f (mc); static f(mc) is not legal in Ada. You can't pass a dynamically tagged operand like "mc" to something that expects a specific type like A_TYPE. This rules helps to avoid confusion about what is and what is not dynamically bound, and to avoid implicit "truncation" or "slicing" as it is sometimes called. Unintended "slicing" is a relatively common source of bugs in C++. : Eiffel (inside A_TYPE): : f m.f dynamic : ... : Conclusion: : Ada: : - more efficient, but : - manual optimisation places a burden on developer This really isn't optimization, in my view. It is simply distinguishing between a specific type and a set of types. I doubt if the programmer is even thinking in terms of optimization. I presume they are more thinking in terms of correctness and maintainability. : - reduced flexibility/reusability I don't agree with this. When dynamic binding is used willy-nilly and without explicit intention, you don't get flexibility, you get a maintenance nightmare. You can exactly match the Eiffel "flexibility" by using dynamic binding explicitly, so the only difference is whether the default is dynamic or static binding. This has more to do with readability and intent, than with more or less flexibility. The rule for what is dynamically bound in Ada is basically just this: You get dynamic binding only when passing a class-wide controlling operand to a dispatching operation. Everything else is statically bound. : ... : Don. -Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ Intermetrics, Inc. Cambridge, MA USA ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Tucker Taft @ 1996-04-04 0:00 ` Tucker Taft 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Don Harrison 1 sibling, 1 reply; 218+ messages in thread From: Tucker Taft @ 1996-04-04 0:00 UTC (permalink / raw) One correction to my correction: Tucker Taft (stt@henning.camb.inmet.com) wrote: : Don Harrison (donh@syd.csa.com.au) wrote: : : In case anyone is confused about how Eiffel's and Ada's parameter mechanisms : : compare, the following hopefully summarizes them: : : SUPPLIER CLIENT BINDING : : -------- ------ ------- : : Assuming: : : Ada: : : type A_TYPE is private -- tagged m: A_TYPE; : : type B_TYPE is private -- tagged mc: A_TYPE'Class; : : n: B_TYPE; : : nc: B_TYPE'Class; : : Eiffel: : : class A_TYPE ... end m: A_TYPE : : class B_TYPE ... end n: B_TYPE : : Case1 : : ----- : : Ada: : : procedure f (a: A_TYPE) f (m); static : : f (mc); dynamic : : But if f not defined in same spec: : : f (m); static : : f (mc); static : f(mc) is not legal in Ada. You can't pass a dynamically tagged operand : like "mc" to something that expects a specific type like A_TYPE. Sorry, I didn't make it clear that only the second f(mc) is illegal. You can't pass a dynamically tagged operand like "mc" to something that expects a specific type like A_TYPE, *unless* it is as part of a dynamically bound call. : This rules helps to avoid confusion about what is and what is not : dynamically bound, and to avoid implicit "truncation" or "slicing" : as it is sometimes called. Unintended "slicing" is a relatively common : source of bugs in C++. This is still the best rule to remember: : You get dynamic binding only when passing a class-wide controlling : operand to a dispatching operation. : Everything else is statically bound. -Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ Intermetrics, Inc. Cambridge, MA USA ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Tucker Taft @ 1996-04-12 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-04-12 0:00 UTC (permalink / raw) Tucker Taft writes: :One correction to my correction: : :Tucker Taft (stt@henning.camb.inmet.com) wrote: : :: Don Harrison (donh@syd.csa.com.au) wrote: [...] :: : Case1 :: : ----- :: : Ada: :: : procedure f (a: A_TYPE) f (m); static :: : f (mc); dynamic : :: : But if f not defined in same spec: :: : f (m); static :: : f (mc); static : :: f(mc) is not legal in Ada. You can't pass a dynamically tagged operand :: like "mc" to something that expects a specific type like A_TYPE. : :Sorry, I didn't make it clear that only the second f(mc) is illegal. :You can't pass a dynamically tagged operand like "mc" to something :that expects a specific type like A_TYPE, *unless* it is as part :of a dynamically bound call. Thanks for the correction. I shall refrain from being naughty by suggesting that you were confused because the mechanism is confusing :-). :: This rules helps to avoid confusion about what is and what is not :: dynamically bound, Yes, it wouldn't be nice for the same signatures to have different semantics. In Eiffel, it is clearer whether an operation dispatches or is classwide wrt an operand by virtue of whether the corresponding class contains it. : and to avoid implicit "truncation" or "slicing" :: as it is sometimes called. Unintended "slicing" is a relatively common :: source of bugs in C++. Nasty! :This is still the best rule to remember: : :: You get dynamic binding only when passing a class-wide controlling :: operand to a dispatching operation. : :: Everything else is statically bound. Succinct. :-Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ :Intermetrics, Inc. Cambridge, MA USA Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Tucker Taft 1996-04-04 0:00 ` Tucker Taft @ 1996-04-12 0:00 ` Don Harrison 1996-04-15 0:00 ` Robert I. Eachus 1 sibling, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-04-12 0:00 UTC (permalink / raw) Tucker Taft) writes: :One correction, and one comment... : :Don Harrison (donh@syd.csa.com.au) wrote: [...] :: Conclusion: : :: Ada: :: - more efficient, but :: - manual optimisation places a burden on developer : :This really isn't optimization, in my view. It is simply :distinguishing between a specific type and a set of types. Yes, it may be that an Ada programmer might choose static binding because they know the entity happens to be of a specific type. This is fine if it is known that the type is absolutely set in concrete. However, we need to consider why it is known. Normally, it will be known because the (transitive) source of the value it last received was itself a statically bound call. That is, one designed to handle a specific abstraction, rather than a family of abstractions; it is not designed for reuse. If the routine servicing the call is designed for reuse, then it returns an indeterminate dynamic type which demands dynamic binding when we use the returned entity as an actual parameter to the operation we are designing. Generally speaking, if there are many opportunities of using entities of specific types, it is indicative of a low level of reuse. I think most would agree that well written software maximises reuse. If the above analysis is valid, it also maximises dynamic binding. :I doubt if the programmer is even thinking in terms of :optimization. I presume they are more thinking in terms :of correctness and maintainability. Don't agree. If reuse, and hence, dynamic binding is maximised (in a safe way: supported by assertions), then so is correctness and maintainability. :: - reduced flexibility/reusability : :I don't agree with this. When dynamic binding is used willy-nilly :and without explicit intention, you don't get flexibility, you get :a maintenance nightmare. No. If you have high reuse with assertions, you have a maintenance dream! : You can exactly match the Eiffel "flexibility" :by using dynamic binding explicitly, so the only difference is whether :the default is dynamic or static binding. This has more to do with :readability and intent, than with more or less flexibility. Yes. The philosophy in Eiffel of maximising reuse means that dynamic binding is chosen as the correct default, IMO. [...] : :-Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ :Intermetrics, Inc. Cambridge, MA USA Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Don Harrison @ 1996-04-15 0:00 ` Robert I. Eachus 1996-04-19 0:00 ` Don Harrison 0 siblings, 1 reply; 218+ messages in thread From: Robert I. Eachus @ 1996-04-15 0:00 UTC (permalink / raw) In article <Dpqpr6.837@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Yes, it may be that an Ada programmer might choose static binding because > they know the entity happens to be of a specific type. This is fine if it > is known that the type is absolutely set in concrete. > However, we need to consider why it is known. Normally, it will be > known because the (transitive) source of the value it last > received was itself a statically bound call. That is, one designed > to handle a specific abstraction, rather than a family of > abstractions; it is not designed for reuse. If the routine > servicing the call is designed for reuse, then it returns an > indeterminate dynamic type which demands dynamic binding when we > use the returned entity as an actual parameter to the operation we > are designing. You worry about a real, but not very major problem. The Ada mechanism is simple, elegant, and results in very reusable code. But it can give you a blinding headache if you try to understand the simple rules that apply to the abstraction and the simple rules that apply to the implementation at the same time. In the abstract view, the programmer just writes Foo(Bar) not worrying most of the time whether Bar is an object marked as a member of a classwide type or a specific type. He knows if he writes: Foo(Bar_Type'CLASS(Bar)) that he will force a dispatching call, but usually he has no reason to do so. Now the compiler comes along. If the controlling operand is statically typed, a non-dispatching call is used. If it is dynamic, a dispatching call occurs. Again simple. Now for the blinding headache... type Foo is tagged ....; function Bar(F: in Foo) return Boolean; procedure Barf(F: in out Foo); procedure Barf(F: in out Foo) is begin if Bar(F) then... end Barf; ... type Foobar is new Foo with ...; function Bar(F: in Foobar) return Boolean; ... X: Foobar; Y: Foo'Class := X; ... Barf(Y); So far so good. But in the (dispatching) call to Barf of Y, the call to Bar is directed to Bar for Foo not Bar for Foobar. Is this a problem? Not really. First of all, equality is treated specially but other redispatching cases seldom occur in real Ada code. Yes, I know that they are common in other OO languages, but in Ada generics are more often used where redispatching is needed in other OO languages. This is really an efficiency issue. Generics can trade the space of mulitple copies of code for the efficiency of eliminating redispatching. Second, in Ada, from experience, the non-redispacthing operation is usually the right one (except for equality, but that is taken care of). But what if we expect to need redispatching above? The body of Barf becomes: procedure Barf(F: in out Foo) is begin if Bar(Foo'Class(F)) then... end Barf; or often better: procedure Barf(F: in out Foo) is Temp: Foo'Class := F begin if Bar(Temp) then... end Barf; More often you run into cases where the explicit redispatch is the right approach, but it usually occurs where no original dispatch is required, so there is no "extra" overhead: procedure Put(Obj: in Object_Type'Class) is begin Put(Default_File, Obj); end Put; pragma Inline(Put); procedure Put(F: in File, Obj: in Object_Type) is... This idiom often occurs, and the pleasant effect for the programmer is to minimize the number of subprograms that may need to be overridden when defining a new subclass. The first Put is classwide, and just defines the behavior of the single operand Put in terms of the two operand version. Or much more to the point: function "+" (L,R: Foo'Class) return Foo_Set is begin return Add(L,R); end "+"; function Add(L: Foo; R: Foo'Class) return Foo_Set is begin return Add(Create_Class(L),R); end Add; ... function Add(FS: Foo_Set; R: Foo) return Foo_Set is... ...and so on. This is the way to do multiple dispatching in Ada. The careful combination of classwide operations and class specific operations allows you, for example, to create a workable heterogeneous set type. And, once you have the base class declared correctly, extending it (subclassing) is fairly simple, since most of the "nuisance" operations are classwide and need no overriding. -- Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-15 0:00 ` Robert I. Eachus @ 1996-04-19 0:00 ` Don Harrison 1996-04-19 0:00 ` Matt Kennel 0 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-04-19 0:00 UTC (permalink / raw) Robert I. Eachus writes: :In article <Dpqpr6.837@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: : : > Yes, it may be that an Ada programmer might choose static binding because : > they know the entity happens to be of a specific type. This is fine if it : > is known that the type is absolutely set in concrete. : : > However, we need to consider why it is known. Normally, it will be : > known because the (transitive) source of the value it last : > received was itself a statically bound call. That is, one designed : > to handle a specific abstraction, rather than a family of : > abstractions; it is not designed for reuse. If the routine : > servicing the call is designed for reuse, then it returns an : > indeterminate dynamic type which demands dynamic binding when we : > use the returned entity as an actual parameter to the operation we : > are designing. : : You worry about a real, but not very major problem. The Ada :mechanism is simple, elegant, and results in very reusable code. But :it can give you a blinding headache if you try to understand the :simple rules that apply to the abstraction and the simple rules that :apply to the implementation at the same time. What I'm basically saying about Eiffel is that it's use would tend to maximise reusability and flexibility in developed software due to the defaults of the language: a) reusability - the default is that a routine is extendable (not frozen) b) flexibilty - the default is that entities are polymorphic, so will maximise (theoretical) dynamic binding when used as actual parameters. This equates to flexibility, IMO. I acknowledge you can do exactly the same things in Ada but believe an Ada programmer would typically use static binding more often than an Eiffel programmer and would tend to use classwide operations more than an Eiffel programmer. If both of these were true, it would be true to say that the resulting Eiffel software would be more reusable and more flexible. The issue of redispatching is a different one, I think. Without addressing what you have said in detail, I would like to comment that I think it is incorrect to allow dispatching to an ancestor routine of an object as in your example: [...] : type Foo is tagged ....; : : function Bar(F: in Foo) return Boolean; : : procedure Barf(F: in out Foo); : : procedure Barf(F: in out Foo) is : begin : if Bar(F) then... : end Barf; : ... : : type Foobar is new Foo with ...; : : function Bar(F: in Foobar) return Boolean; : ... : : X: Foobar; : Y: Foo'Class := X; : ... : : Barf(Y); : : So far so good. But in the (dispatching) call to Barf of Y, the :call to Bar is directed to Bar for Foo not Bar for Foobar. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ : Is this a :problem? Not really. Without thinking too hard about it, I think this could cause problems. IMO, binding should always be to the routine defined for the class corresponding to the object (unless the operation is classwide, of course). The object has specific characteristics which may need special attention that the ancestor routine will not provide An analogy of the problem might be: It's fine to use a chainsaw to cut down a tree, but you'll have problems if you try to use it for timber craftwork instead of a fretsaw. You may argue that the ancestor routine may redispatch to the correct routine but an ancestor should have no knowledge of it's descendants. This is the chicken before the egg. Eiffel always dispatches to the operation designed for the object. [deleted for brevity] : More often you run into cases where the explicit redispatch is the :right approach, but it usually occurs where no original dispatch is :required, so there is no "extra" overhead: : : procedure Put(Obj: in Object_Type'Class) is : begin Put(Default_File, Obj); end Put; : pragma Inline(Put); : : procedure Put(F: in File, Obj: in Object_Type) is... You would do this in Eiffel with a combination of frozen and normal features. [...] :-- : : Robert I. Eachus : :with Standard_Disclaimer; :use Standard_Disclaimer; :function Message (Text: in Clever_Ideas) return Better_Ideas is... Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Don Harrison @ 1996-04-19 0:00 ` Matt Kennel 1996-04-20 0:00 ` Bob Hathaway 1996-04-23 0:00 ` Don Harrison 0 siblings, 2 replies; 218+ messages in thread From: Matt Kennel @ 1996-04-19 0:00 UTC (permalink / raw) Don Harrison (donh@syd.csa.com.au) wrote: : What I'm basically saying about Eiffel is that it's use would tend to maximise : reusability and flexibility in developed software due to the defaults of the : language: : a) reusability - the default is that a routine is extendable (not frozen) : b) flexibilty - the default is that entities are polymorphic, so will maximise : (theoretical) dynamic binding when used as actual parameters. : This equates to flexibility, IMO. : I acknowledge you can do exactly the same things in Ada but believe an Ada : programmer would typically use static binding more often than an Eiffel : programmer and would tend to use classwide operations more than an Eiffel : programmer. I'm an Eiffel fan but I'd disagree here. "polymorphism" is not like alms for the poor. Per se more 'polymorphism' doesn't mean 'better', it just means 'more polymorphism'. Personally I have lots of success with the Sather approach, where you can use concrete classes when you need to be specific about the type and abstract classes when you don't. These concrete classes are always available as sources for future implementation. You can get the same effect in Eiffel if the compiler has a common optimization. Never inherit from those classes which you want to use as a "final" concrete non-dispatching version. If you want to use their implementation in the future, then get it from a differently named superclass. If the compiler notices that a class has no descendants (and this fact can be discerned at at compile time) then it should not have to dispatch calls. So, if you see a class FOOBAR and you want to use it like it's concrete: make a new class MY_SPECIAL_CONCRETE_FOOBAR and inherit FOOBAR and don't do anything else. Very simple. The only difference in Sather is that you needn't make a separate concrete class from which you get implementation: a concrete class is both 'final' (not dispatchable) and remains a source of implementation for others. : Don. matt ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Matt Kennel @ 1996-04-20 0:00 ` Bob Hathaway 1996-04-23 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Bob Hathaway @ 1996-04-20 0:00 UTC (permalink / raw) In article <4l92n3$1g6@gaia.ns.utk.edu>, Matt Kennel <kennel@msr.epm.ornl.gov> wrote: >>... >... >I'm an Eiffel fan but I'd disagree here. "polymorphism" is not like alms >for the poor. Per se more 'polymorphism' doesn't mean 'better', it just >means 'more polymorphism'. I have to disagree. Having used multiple-polymorphism in a class-based language I have found it to be far more powerful. I haven't been following this thread and its been a long time since I've programmed in Ada (before 9x), so could some kind sole remind me if class wide types are essentially true types, meaning that the true types of arguments are not lost, for future calls for instance. I seem to recall class wide types are not lost and multiple-polymorphism is provided in this case, at least for the class-wide parameter types. Take a look at my quick but hopefully convincing argument for a true type system and multiple-polymorphism in http://www.sigs.com/objectcurrents, issue 3 (I think) on True Types And Multiple Polymorphism as it presents my argument on this issue. I see single-polymorphism as a static efficiency measure or for an approach to enforcing strong static typechecking (although it's certainly not the only one) and part of the object message paradigm which I see as best for MP too, at least for objects. I welcome comments. Best Regards! Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Matt Kennel 1996-04-20 0:00 ` Bob Hathaway @ 1996-04-23 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-04-23 0:00 UTC (permalink / raw) Matt Kennel writes: :Don Harrison (donh@syd.csa.com.au) wrote: :: What I'm basically saying about Eiffel is that it's use would tend to maximise :: reusability and flexibility in developed software due to the defaults of the :: language: : :: a) reusability - the default is that a routine is extendable (not frozen) :: b) flexibilty - the default is that entities are polymorphic, so will maximise :: (theoretical) dynamic binding when used as actual parameters. :: This equates to flexibility, IMO. : :: I acknowledge you can do exactly the same things in Ada but believe an Ada :: programmer would typically use static binding more often than an Eiffel :: programmer and would tend to use classwide operations more than an Eiffel :: programmer. : :I'm an Eiffel fan but I'd disagree here. "polymorphism" is not like alms :for the poor. Per se more 'polymorphism' doesn't mean 'better', it just :means 'more polymorphism'. My naive reasoning is that if an entity is polymorphic, it can be used in more places than a non-polymorphic one because it represents a family of types rather than a specific type. Isn't that entity more flexible to use? What do others think? :Personally I have lots of success with the Sather approach, where you can :use concrete classes when you need to be specific about the type and :abstract classes when you don't. Jon seems to suggest that Sather's concrete and abstract classes are similar to Ada's specific and classwide types. How are they different? Is there any similarity with Eiffel deferred classes? :These concrete classes are always available as sources for future :implementation. : :You can get the same effect in Eiffel if the compiler has a common :optimization. ... of static binding for routines which are not redefined in descendants? : Never inherit from those classes which you want to use as a :"final" concrete non-dispatching version. If you want to use their :implementation in the future, then get it from a differently named :superclass. So, it's freezing on class rather than routine basis? :If the compiler notices that a class has no descendants (and this fact can :be discerned at at compile time) then it should not have to dispatch :calls. : :So, if you see a class FOOBAR and you want to use it like it's concrete: :make a new class MY_SPECIAL_CONCRETE_FOOBAR and inherit FOOBAR and :don't do anything else. Very simple. : :The only difference in Sather is that you needn't make a separate :concrete class from which you get implementation: a concrete class :is both 'final' (not dispatchable) and remains a source of implementation :for others. So, you may inherit from it (but not redefine it's features)? Would this be equivalent to freezing all the features in a class? If that is so, Eiffel offers a finer granularity of control. Typically, in Eiffel, you would define frozen and normal routines within the same class rather than define a separate class. Can you give some examples? :: Don. : :matt Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Tucker Taft @ 1996-04-04 0:00 ` Jon S Anthony 1996-04-04 0:00 ` Laurent Guerby 1996-04-04 0:00 ` Robb Nebbe 3 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-04-04 0:00 UTC (permalink / raw) In article <DpBIEE.L5u@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > In case anyone is confused about how Eiffel's and Ada's parameter mechanisms > compare, the following hopefully summarizes them: [reasonably synopsis of binding time stuff for Ada and Eiffel, though misleading in the case of class wide types for the uninitiated] But, > Conclusion: > > Ada: > - more efficient, but > - manual optimisation places a burden on developer > - reduced flexibility/reusability > - syntactically complex > > Eiffel: > - less efficient, but > - automatic optimisation means that the the efficiency gap is reduced as is > the burden on developer > - greater flexibility/reusability > - syntactically simple I'm not clear on why you keep saying this - well, aside from evangelizing that is. I'm not clear on why you think that localizing polymorphism is somehow "manual optimization"? Sure the Ada compiler can do similar analysis as an Eiffel compiler if everything is always a polymorphic call, and you can do this if you want and be equivalent to Eiffel, so this seems irrelevant. That is, the Eiffel case is a subclass of what you can do in Ada. I think the localization of polymorphism revolves around clarity of code and maintenance issues. That's certainly what I like about it and why I think it clearer than the Eiffel/Et.Al. approach. [Aside: Note that Sather is _more_ like Ada in this respect than Eiffle. I guess this is one reason why I think Sather is a _significant_ improvement over Eiffel. Hey, there adding modules too! And it has those way cool iterators and... Now, if only there were some industrial strength implmentations, I might be tempted...] Second, what's less flexible or "reusable"? I know you keep saying this but nothing you've said has any convincing content - or maybe I've missed something. Maybe you think that because a class wide operation is static binding it somehow prevents polymorphic values/processing?? That is not true. For example, > procedure i (a: A_TYPE'Class; b: B_TYPE'Class) i (m, n) static > i (m, nc) static > i (mc, n) static > i (mc, nc) static Note that i can be passed _anything_ in the type trees rooted at A_Type and B_Type (including A_Type'Class and B_Type'Class). How is the Eiffel more flexible/reusable?? In fact, it would seem that this approach is _more_ flexible and _robust_ to changes (more like Sather abstract types) Lastly, I don't understand why you think that the function style is more "syntactically complex". I suppose you just have a preference and describe this as an objective fact, but that is not particularly convincing. /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] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Tucker Taft 1996-04-04 0:00 ` Jon S Anthony @ 1996-04-04 0:00 ` Laurent Guerby 1996-04-04 0:00 ` Robb Nebbe 3 siblings, 0 replies; 218+ messages in thread From: Laurent Guerby @ 1996-04-04 0:00 UTC (permalink / raw) Don Harrison writes : In case anyone is confused about how Eiffel's and Ada's parameter mechanisms : compare, the following hopefully summarizes them: [deleted] : Ada: : procedure f (a: A_TYPE) f (m); static : f (mc); dynamic : : But if f not defined in same spec: : f (m); static : f (mc); static What does "not defined in same spec" refer to ? [Ada question] [deleted] [thanks for this interesting list] : Conclusion: : : Ada: : - more efficient, but : - manual optimisation places a burden on developer : - reduced flexibility/reusability : - syntactically complex : : Eiffel: : - less efficient, but : - automatic optimisation means that the the efficiency gap is reduced as is : the burden on developer : - greater flexibility/reusability : - syntactically simple : : Don. - Efficiency/optimisation : your list shows that for most uses, the Ada programmer can _force_ a static binding. In the case of dynamic binding, the Ada compiler can do the same optimisation (not 100% sure of complete equivalence here ;-) than the Eiffel compiler. - flexibility : I don't know flexibility means weak compile-time checks here (ultimate flexibility is int and void * for this respect ;-). On the Ada side, you have 4 possibilities (at least, staying in the Ada/OOP world ;-) : X1 : Tagged_Type; X2 : Access_To_Tagged_Type; X3 : Tagged_Type'Class := Something_Not_Statically_Tagged; X4 : Access_To_Tagged_Type_Class; If you choose to use X1 or X2, you say to the compiler (and any source reader, human or not, "maintenability" feature) that X1 and X2.all are _exactly_ of type Tagged_Type (not in a subclass), since the tag never change for an Ada object. That's a semantic attribute, checked at compile time (in the Ada way ;-). To some extend, it's a "flexible" feature. If you use X3, you say to the compiler (idem) that you want a stack allocated "dynamically" (at elaboration time, no tag change after it) object somewhere in the Tagged_Type classes and subclasses. Note that the object will be "garbage collected" at the end of the declaration scope. If you use X4, you have complete "flexibility", and you have to use dynamic allocation with "new", or provided constructors. Note that you'll have to use Controlled types to provide portable "automatic" storage reclamation, or you'll have to buy a compiler supporting the 13.11.3 optional feature, widely known under the name "automatic garbage collection" ;). I don't know much of the Eiffel equivalent, as I'm far to be an Eiffel expert (but will be happy to learn more), I've just read a few years ago Bertrand Meyer's book. - reusability : I don't clearly see how the listed features have influence on reusability. Of course restricting the set of possible classes for a declared object "reduces" reutilisability, but if you code a reusable component in Ada, you'll certainly make great use of access to class wide types. - syntactically : That's of course a matter of state ("sugar" ;-). The Ada notation looks less "oopish", but has some advantage when dealing with symetric operators ("+", etc ...) as pointed out in other threads. [Don't trust 100% of what I say about OOP in Ada, since I haven't a great experience of OOP/Ada, please correct my mistakes if any in a constructive way]. -- -- Laurent Guerby, student at Telecom Bretagne (France), Team Ada -- "Use the Source, Luke. The Source will be with you, always (GPL)" -- http://www-eleves.enst-bretagne.fr/~guerby/ (GATO Project) -- Try GNAT, the GNU Ada 95 compiler (ftp://cs.nyu.edu/pub/gnat) ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison ` (2 preceding siblings ...) 1996-04-04 0:00 ` Laurent Guerby @ 1996-04-04 0:00 ` Robb Nebbe 3 siblings, 0 replies; 218+ messages in thread From: Robb Nebbe @ 1996-04-04 0:00 UTC (permalink / raw) Don Harrison wrote: > Conclusion: > > Ada: > - more efficient, but > - manual optimisation places a burden on developer > - reduced flexibility/reusability > - syntactically complex > > Eiffel: > - less efficient, but > - automatic optimisation means that the the efficiency gap is > reduced as is the burden on developer > - greater flexibility/reusability > - syntactically simple > > Don. These conclusions are in direct conflict with my experience. My experience is that was is basically going on in Ada is that the programmer is allowed to make more of the semantics explict rather than implicit. I get to tell the compiler things I already know instead of having the compiler guess at them. I experience no burden because of Ada's approach. I also haven't found any real difference in flexibility or reusability due to Ada's distinction between class-wide and specific types. I do think that it helps a lot in understanding someone else's code but this is really a consequence of having more semantic information presented to me rather than having to figure it out. I don't think you have a sufficient understanding of the approach taken by Ada yet. You seem to have most of the facts but you don't seem to have synthesised it into a coherent whole. If you understand the separation between interface and implementation inheritance and adopt a programming style that makes heavy use of abstract classes (this should sound a little like Sather) then the distinction practically falls in your lap. Robb Nebbe ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-22 0:00 ` Don Harrison 1996-03-22 0:00 ` Norman H. Cohen 1996-03-22 0:00 ` Norman H. Cohen @ 1996-03-23 0:00 ` Joachim Durchholz 1996-03-26 0:00 ` Norman H. Cohen 1996-04-02 0:00 ` Detecting type mismatch at compile time (was Re: Real OO) Robert I. Eachus 1996-03-28 0:00 ` Real OO Joachim Durchholz 3 siblings, 2 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-03-23 0:00 UTC (permalink / raw) ncohen@watson.ibm.com wrote 22.03.96 on Re: Real OO: > ...a specific type as an Eiffel reference whose dynamic type is known to be > identical to its static type. A method call can be far more efficient if > this knowledge is conveyed to the compiler, and it is common in practice > for this knowledge to be available. The Eiffel paradigma is that it should *not* be possible to fix a formal parameter's dynamic type to its static types. Such a thing is considered an unnecessary limitation introduced on the routine, for efficiency reasons that aren't even valid because a compiler can determine wether such an optimization is possible. In fact, all Eiffel literature on the subject strongly encourages compiler writers to replace dynamic cally by static calls whenever possible, and to inline code wherever it makes sense. > For a one-parameter subprogram, the effect of a classwide subprogram > could be achieved by a dispatching subprogram that is never overridden. Agreed. > Making it classwide simply documents the fact that the algorithm does not > depend on the tag of the parameter, and causes the compiler to catch any > attempt to override. This assumes that the writer of a module knows that his routines never need to be overridden. While this may be true when writing a program with a fixed and well-understood task, this isn't true when writing libraries, or when programs are modified in the future and in ways that weren't anticipated. Considering the fact that fixed and well-understood tasks don't need OO (structured programming and functional decomposition should suffice for this type of tasks), this argument gains some weight. > The real power of a classwide program arises with > multiple parameters. In Eiffel, dispatching is controlled by the one > object whose method is invoked (the x in x.f()). In an Ada dispatching > subprogram, there can be multiple parameters controlling the dispatching. > > [Omitting details on dispatching based on the dynamic type of multiple > parameters] The type of dispatching noted here can easily be achieved with anchored types in Eiffel. In an Ada-style notation, this would look as follows: function Corresponding_Parts_Equal (Self: Shape_Type; Other: like Self) return Boolean The "like Self" part requires Other to have the same dynamic type as Self, or a type that is a descendant of Self's type. I'm not sure wether this is enough to handle all cases that arise in practice, but it goes a long way. BTW I see serious problems with the Congruent function given. Consider descandant types "Filled_Circle", "Hatched_Circle" etc. - with the understanding that these don't have any new geometric properties that should influence Corresponding_Parts_Equal or Congruent. First of all, it would be necessary to define a Corresponding_Parts_Equal function for each pair of Circle_Type descendants - this can become quite a formidable task if the number of direct and indirect descandants grows. In addition, the Congruent code will break - Shape1'Tag and Shape2'Tag will not be equal, making Congruent return false even if a Filled_Circle and a Hatched_Circle actually have equal corresponding parts. -Joachim -- Im speaking for myself here. ## CrossPoint v3.1 ## ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-23 0:00 ` Joachim Durchholz @ 1996-03-26 0:00 ` Norman H. Cohen 1996-04-04 0:00 ` Don Harrison ` (3 more replies) 1996-04-02 0:00 ` Detecting type mismatch at compile time (was Re: Real OO) Robert I. Eachus 1 sibling, 4 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-03-26 0:00 UTC (permalink / raw) In article <65O34ib-3RB@herold.franken.de>, jhd@herold.franken.de (Joachim Durchholz) writes: |> ncohen@watson.ibm.com wrote 22.03.96 on Re: Real OO: |> |> > ...a specific type as an Eiffel reference whose dynamic type is known to be |> > identical to its static type. A method call can be far more efficient if |> > this knowledge is conveyed to the compiler, and it is common in practice |> > for this knowledge to be available. |> |> The Eiffel paradigma is that it should *not* be possible to fix a formal |> parameter's dynamic type to its static types. Such a thing is considered |> an unnecessary limitation introduced on the routine, for efficiency |> reasons that aren't even valid because a compiler can determine wether |> such an optimization is possible. The Ada rule under discussion--that a subprogram call is not dispatching unless the relevant ACTUAL parameters belong to a classwide type--does not limit the called routine at all. It provides the opportunity for the CALLER, calling from a context in which the dynamic type is known, to declare this knowledge. The Eiffel optimization you describe is a difficult one because in practice it is difficult for an optimizing compiler to resolve pointer-induced aliasing and determine the dynamic type at compile time. The analysis required is expensive, requiring interprocedural analysis to be effective, and the payoff is rare, so it is not clear that it is worthwhile for an optimizing compiler to perform this analysis routinely. (See, for example, P. R. Carini, H. Srinivasan, and M. Hind, "Flow-Sensitive Type Analysis for C++", IBM Research Report RC 20267, http://www.watson.ibm.com:8080/PS/7899.ps.gz.) In Ada, the programmer conveys, not through any special hint-giving pragma, but just through the natural course of declaring an object to belong to a specific type, that a call should not be dispatching. |> > For a one-parameter subprogram, the effect of a classwide subprogram |> > could be achieved by a dispatching subprogram that is never overridden. |> |> Agreed. |> |> > Making it classwide simply documents the fact that the algorithm does not |> > depend on the tag of the parameter, and causes the compiler to catch any |> > attempt to override. |> |> This assumes that the writer of a module knows that his routines never |> need to be overridden. No, it assumes that the writer of a module knows that a particular routine in the module accomplishes its purpose without directly exploiting knowledge of a given object's dynamic type, or tag. (The routine may invoke other routines that dispatch based on the dynamic type, but it does not itself differentiate among different dyanamic types.) |> While this may be true when writing a program with |> a fixed and well-understood task, this isn't true when writing libraries, |> or when programs are modified in the future and in ways that weren't |> anticipated. No routine, even in an object-oriented program, is immune from future modification resulting from new requirements that change the routine's preconditions and postconditions. However, a classwide routine will generally not have to be modified just because a new type was added to the class hierarchy. A classwide routine is, by definition, one whose correctness does not depend on the dynamic types of the objects it is dealing with. |> Considering the fact that fixed and well-understood tasks don't need OO |> (structured programming and functional decomposition should suffice for |> this type of tasks), this argument gains some weight. That's a rather limited view of the utility of OO techniques! Even fixed and well understood tasks benefit from data abstraction and encapsulation, from the use of inheritance to avoid redundant effort, and from the use of polymorphism to facilitate generalized algorithms. Like any other program, an OO program reflects a model of the problem domain and the ways in which it is likely to evolve. If the problem domain evolves in the ways that the original designers anticipated, the program can be updated to reflect this evolution simply by the addition of new subclasses. But if the problem domain evolves in ways inconsistent with the original designer's model, no OO technique is going to save your code from major surgery. Concerning dispatching based on multiple parameters: |> The type of dispatching noted here can easily be achieved with anchored |> types in Eiffel. In an Ada-style notation, this would look as follows: |> |> function Corresponding_Parts_Equal |> (Self: Shape_Type; Other: like Self) |> return Boolean |> |> The "like Self" part requires Other to have the same dynamic type as Self, |> or a type that is a descendant of Self's type. (Are you sure it's the DYNAMIC type? Section 11.4.3 of Meyer's _Object_Oriented_Software_Construction_ seems to suggest that it's the declared type.) It's the "or a type that is a descendant of" that differentiates the Eiffel approach from the Ada approach. |> I'm not sure wether this is enough to handle all cases that arise in |> practice, but it goes a long way. Taking your word that the behavior is based on the dynamic type, this approach introduces a disturbing assymmetry. Peeking ahead to the example you introduce below, if the dynamic types of C, FC, and HC are Circle_Type, Filled_Circle_Type, and Hatched_Circle_Type, respectively, then the following calls (reverting to Eiffel notation) give a sensible answer: C.Corresponding_Parts_Equal(C) C.Corresponding_Parts_Equal(FC) C.Corresponding_Parts_Equal(HC) FC.Corresponding_Parts_Equal(FC) HC.Corresponding_Parts_Equal(HC) The following calls violate the requirement that the dynamic type of Other be descended (in zero or more steps) from the dynamic type of Self, and presumably result in a run-time error: FC.Corresponding_Parts_Equal(C) HC.Corresponding_Parts_Equal(C) FC.Corresponding_Parts_Equal(HC) HC.Corresponding_Parts_Equal(FC) The problem with this assymetry is not merely aesthetic: It greatly complicates the programming of a routine designed to exploit the "or a type that is a descendant of" property of Corresponding_Parts_Equal, because that routine must determine which object's type, if any, is at least as general as the other object's type. |> BTW I see serious problems with the Congruent function given. |> |> Consider descandant types "Filled_Circle", "Hatched_Circle" etc. - with |> the understanding that these don't have any new geometric properties that |> should influence Corresponding_Parts_Equal or Congruent. I was using the term "shape" to mean just that, an abstract entity in plane geometry, not the representation of a shape as a graphic. I would think of Filled_Circle and Hatched_Circle as instances of a class Graphic_Type, COMPOSED FROM a Shape_Type component and a Fill_Type component: type Graphic_Type is tagged record Shape : Shape_Type; Fill : Fill_Type; end record; If one is interested in congruence of Graphic_Type instances, that's a very different notion from congruence of abstract geometric figures, but it's easily implemented as a classwide operation: function Congruent (Graphic_1, Graphic_2: Graphic_Type'Class) return Boolean is begin return Congruent (Graphic_1.Shape, Graphic_2.Shape); end Congruent; If Graphic_Type is later extended, the function above can sensibly be applied to any object in any type in the hierarchy, because all such objects have Shape components. Nonetheless, let's persue the consequences of trying to extend Circle_Type (a descendant of Shape_Type) to include such subclasses as Filled_Circle_Type and Hatched_Circle_Type. |> First of all, it would be necessary to define a Corresponding_Parts_Equal |> function for each pair of Circle_Type descendants - this can become quite |> a formidable task if the number of direct and indirect descandants grows. No, this would not be necessary, as explained below. |> In addition, the Congruent code will break - Shape1'Tag and Shape2'Tag |> will not be equal, making Congruent return false even if a Filled_Circle |> and a Hatched_Circle actually have equal corresponding parts. True. If I had intended to allow shapes to be specialized to include nongeometric features, I would add a new abstract method to Shape_Type: function Geometry (Special_Shape: Shape_Type) return Shape_Type'Class is abstract; I would override it for Circle_Type as follows: function Geometry (Special_Circle: Circle_Type) return Shape_Type'Class is Result: Circle_Type := Special_Circle; begin return Result; end Geometry; This function always returns an object whose dynamic type, or tag, is that of Circle_Type. Types extending Circle_Type would inherit this function without overriding it. Thus if FC is a Shape_Type'Class object with the dynamic type (tag) of Filled_Circle_Type, Geometry(FC) would dispatch to the function body above and would return the corresponding (truncated) Circle_Type object. Similar definitions of Geometry would be given for all types in the hierarchy containing only geometric information (e.g., Rectangle_Type, but not Filled_Rectangle_Type). The Congruent function would be rewritten as follows: function Congruent (Shape_1, Shape_2: Shape_Type'Class) return Boolean is Shape_1_Geometry : Shape_Type'Class := Geometry(Shape_1); Shape_2_Geometry : Shape_Type'Class := Geometry(Shape_2); begin return Shape_1_Geometry'Tag = Shape_2_Geometry'Tag and then Corresponding_Parts_Equal(Shape_1_Geometry, Shape_2_Geometry); end Congruent; This function will return True when invoked with a filled circle and a hatched circle having the same radius. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-26 0:00 ` Norman H. Cohen @ 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Jon S Anthony ` (3 more replies) 1996-04-09 0:00 ` Joachim Durchholz ` (2 subsequent siblings) 3 siblings, 4 replies; 218+ messages in thread From: Don Harrison @ 1996-04-04 0:00 UTC (permalink / raw) Norman H. Cohen wrote: :In article <65O34ib-3RB@herold.franken.de>, jhd@herold.franken.de :(Joachim Durchholz) writes: [...] :No routine, even in an object-oriented program, is immune from future :modification resulting from new requirements that change the routine's :preconditions and postconditions. However, a classwide routine will :generally not have to be modified just because a new type was added to :the class hierarchy. No routine of any kind - frozen (classwide) or redefinable - needs to be modified because descendants are declared; it is only necessary if the design of the parent changes. : A classwide routine is, by definition, one whose :correctness does not depend on the dynamic types of the objects it is :dealing with. Apart from the efficiency aspect, I wonder is the eagerness of Ada programmers to freeze routines related to the fact that assertions aren't available to: - guard against errors in parents being propagated into descendant classes, and - correctness in parents being compromised in descendants? ;-) :|> Considering the fact that fixed and well-understood tasks don't need OO :|> (structured programming and functional decomposition should suffice for :|> this type of tasks), this argument gains some weight. : :That's a rather limited view of the utility of OO techniques! Even fixed :and well understood tasks benefit from data abstraction and :encapsulation, from the use of inheritance to avoid redundant effort, and :from the use of polymorphism to facilitate generalized algorithms. : :Like any other program, an OO program reflects a model of the problem :domain and the ways in which it is likely to evolve. If the problem :domain evolves in the ways that the original designers anticipated, the :program can be updated to reflect this evolution simply by the addition :of new subclasses. But if the problem domain evolves in ways :inconsistent with the original designer's model, no OO technique is going :to save your code from major surgery. With such an appreciation of OO, you ought to be a big fan of Eiffel! :Concerning dispatching based on multiple parameters: : :|> The type of dispatching noted here can easily be achieved with anchored :|> types in Eiffel. In an Ada-style notation, this would look as follows: :|> :|> function Corresponding_Parts_Equal :|> (Self: Shape_Type; Other: like Self) :|> return Boolean :|> :|> The "like Self" part requires Other to have the same dynamic type as Self, :|> or a type that is a descendant of Self's type. : :(Are you sure it's the DYNAMIC type? Section 11.4.3 of Meyer's :_Object_Oriented_Software_Construction_ seems to suggest that it's the :declared type.) No. It's the static type. Further, if you say 'like Current', it is the static type of the class in which the routine is defined. The purpose of anchored types is to define type dependence and to do it once only. The dependence is propagated into descendants automatically. Otherwise, you would have to redefine such dependence even if implementations were otherwise identical (not that they would be in this example): - in class SHAPE_TYPE: corresponding_parts_equal (other: SHAPE_TYPE): BOOLEAN is do ... end - in class RECTANGLE_TYPE: corresponding_parts_equal (other: RECTANGLE_TYPE): BOOLEAN is do ... end BTW, I notice Ada people have a penchant for humungous identifiers. Is this an unconscious attempt to balance out the heavy syntax? ;-) :It's the "or a type that is a descendant of" that differentiates the :Eiffel approach from the Ada approach. ... and makes the Eiffel approach more flexible (and robust: Ada raises a Constraint_Error exception if the dynamic types of multiple controlling operands differ RM95 3.9.2 (16) ). [discussion about lack of symmetry omitted because it is erroneously assumes runtime conformance to dynamic rather than static types] :-- :Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison @ 1996-04-04 0:00 ` Jon S Anthony 1996-04-12 0:00 ` Don Harrison 1996-04-08 0:00 ` Norman H. Cohen ` (2 subsequent siblings) 3 siblings, 1 reply; 218+ messages in thread From: Jon S Anthony @ 1996-04-04 0:00 UTC (permalink / raw) In article <DpBsG4.MIE@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Norman H. Cohen wrote: > > :In article <65O34ib-3RB@herold.franken.de>, jhd@herold.franken.de > :(Joachim Durchholz) writes: > > : A classwide routine is, by definition, one whose > :correctness does not depend on the dynamic types of the objects it is > :dealing with. > > Apart from the efficiency aspect, I wonder is the eagerness of Ada > programmers to freeze routines related to the fact that assertions > aren't available to: - guard against errors in parents being > propagated into descendant classes, and - correctness in parents > being compromised in descendants? ;-) I think you are confused here. Don't start thinking that Eiffel's "frozen" feature is the equivalent of a classwide operation. It is not. Classwide operations are more general and flexible and cover other sorts of ground beyond what you have with simple frozen features. The notion of classwide types is more similar to Sather abstract types (though there are differences there too), and how they behave wrt to operations. > With such an appreciation of OO, you ought to be a big fan of Eiffel! > Or better yet, Ada! Or Sather... > :|> The "like Self" part requires Other to have the same dynamic type as Self, > :|> or a type that is a descendant of Self's type. > : > :(Are you sure it's the DYNAMIC type? Section 11.4.3 of Meyer's > :_Object_Oriented_Software_Construction_ seems to suggest that it's the > :declared type.) > No. It's the static type. Further, if you say 'like Current', it is > the static type of the class in which the routine is defined. The > purpose of anchored types is to define type dependence and to do it > once only. The dependence is propagated Well, now that is odd as Joachim disputes your claim and sites an ETL reference that it is indeed _dynamic_. > :It's the "or a type that is a descendant of" that differentiates the > :Eiffel approach from the Ada approach. > > ... and makes the Eiffel approach more flexible (and robust: Ada raises a > Constraint_Error exception if the dynamic types of multiple controlling > operands differ RM95 3.9.2 (16) ). Not if you are using classwide types and operations. You can't even _have_ multiple controlling parameters in Eiffel so it is certainly no more "flexible" here. And what's more, there are several cases where Eiffel either a) ensures dynamic type integrity with runtime errors or b) tries to determine this with the infamous system validity check. (which many (most??) implementations do not even try to do. Yes, I know there is/was some attempt to remove this, but the impression is it is still a problem). /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] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Jon S Anthony @ 1996-04-12 0:00 ` Don Harrison 1996-04-17 0:00 ` Jon S Anthony 0 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-04-12 0:00 UTC (permalink / raw) Jon S Anthony writes: :In article <DpBsG4.MIE@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: [...] :> Apart from the efficiency aspect, I wonder is the eagerness of Ada :> programmers to freeze routines related to the fact that assertions :> aren't available to: - guard against errors in parents being :> propagated into descendant classes, and - correctness in parents :> being compromised in descendants? ;-) : :I think you are confused here. Don't start thinking that Eiffel's :"frozen" feature is the equivalent of a classwide operation. It is :not. Classwide operations are more general and flexible and cover :other sorts of ground beyond what you have with simple frozen features. Yes, Eiffel's frozen routines alone are not the equivalent of Ada's classwide operations. The Eiffel analogue is a combination of: a) frozen routines, which make a routine classwide for Current, and b) parameters, all of which are classwide by default, and c) results of functions, which are classwide by default. This provides the same generality and flexibility of Ada's classwide operations. Note that it is really meaningless to speak of 'classwide operations' but rather of classwide entities because a routine may be dispatching wrt one parameter (Current) but is classwide wrt the parameters and the result (if any). The following table shows some equivalent operations in Eiffel and Ada. They are dispatching wrt some parameters and classwide wrt others (eg. 2. below). Ada Eiffel ------------------------------------------------------------------------------------ Assuming type T is private; -- tagged class T ... end type U is private; -- tagged class U ... end ------------------------------------------------------------------------------------ (in class T) (in class U) 1. procedure f (a: T'Class) frozen f 2. procedure g (a: T; b: U'Class) g (b: U) 3. procedure h (a: T'Class; b: U'Class) frozen h (b: U) frozen h (a: T) 4. function i (a: T'Class) return T'Class frozen i: like Current 5. function j (b: U'Class) return T'Class frozen j : T [...] :Well, now that is odd as Joachim disputes your claim and sites an :ETL reference that it is indeed _dynamic_. If he did, then I missed it. :> :It's the "or a type that is a descendant of" that differentiates the :> :Eiffel approach from the Ada approach. :> :> ... and makes the Eiffel approach more flexible (and robust: Ada raises a :> Constraint_Error exception if the dynamic types of multiple controlling :> operands differ RM95 3.9.2 (16) ). : :Not if you are using classwide types and operations. You can't even :_have_ multiple controlling parameters in Eiffel so it is certainly no :more "flexible" here. Nor would we want to. One operand, Current, is more than adequate to convey the information in accordance with the OO principle of uniqueness. In Ada, with the second, third, fourth, ... and nth operands of the same specific type controlling dispatching, by the time the compiler gets to the end of the routine signature, it is rolling it's eyes and crying: "Good grief, I heard you the first time!!!" : And what's more, there are several cases where :Eiffel either a) ensures dynamic type integrity with runtime errors or :b) tries to determine this with the infamous system validity check. :(which many (most??) implementations do not even try to do. Yes, I :know there is/was some attempt to remove this, but the impression is :it is still a problem). You are referring to catcalls: calls which fail because a descendant class makes changes which render the call non-polymorphic wrt ancestors. The changes may be: a) a type has been redefined to a non-conformant type, or b) a routine has been made inaccessible due to a change in it's export status. At first sight, it seems silly to permit such things if they can cause problems with polymorphism but there is a worthy motivation for providing such flexibility: allowing reuse of legacy code that does not exactly fit your requirements. I suspect it's only a problem in Eiffel because other languages don't provide that flexibility. Obviously, if Eiffel were more restrictive, it would not be a problem. But flexibility and reuse are regarded as paramount, so the issue arises. It's an acknowledged problem which is currently being addressed but I don't know how. Hopefully, the solution will retain flexibility and efficiently prevent such calls being made. :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Don Harrison @ 1996-04-17 0:00 ` Jon S Anthony 1996-04-19 0:00 ` Don Harrison 0 siblings, 1 reply; 218+ messages in thread From: Jon S Anthony @ 1996-04-17 0:00 UTC (permalink / raw) In article <DpqJKL.76E@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Jon S Anthony writes: > > :In article <DpBsG4.MIE@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: >... > :I think you are confused here. Don't start thinking that Eiffel's > :"frozen" feature is the equivalent of a classwide operation. It is > :not. Classwide operations are more general and flexible and cover > :other sorts of ground beyond what you have with simple frozen features. > > Yes, Eiffel's frozen routines alone are not the equivalent of Ada's classwide > operations. The Eiffel analogue is a combination of: > > a) frozen routines, which make a routine classwide for Current, and > b) parameters, all of which are classwide by default, and > c) results of functions, which are classwide by default. > > This provides the same generality and flexibility of Ada's classwide > operations. I don't see how. For one thing clients can't define such things without introducing gratuitous/extraneous classes (I would say this is one of the more important uses). For another, you need some clairvoyance when designing a class in determining the set of features to freeze in order to use them in these sorts of combinations elsewhere. This is a real "no no" ala C++ "virtual" tagging. From what I can tell, Eiffel just plain does not have the equivalent flexible capability here. Again, class wide types and their operations are more like Sather abstract types and ops on them. Eiffel does not have these... > equivalent operations in Eiffel and Ada. They are dispatching wrt > some parameters and classwide wrt others (eg. 2. below). >... Hmmm, actually as I understand the semantics of Like Current, I would say that in general these examples are _not_ equivalent. Like Current seems to require the type of the actual/result must be the same as the type of the specific object on which the particular invocation has been made. No such restriction is true in the Ada case - the result can be any type in the tree rooted at T. Actually, this is a little fuzzy, since there is some other talk in ETL where I think you could read Like Current as allowing for anything _conformant_ to the type of the object on which the invocation has been made. Anyone know the real scoop?? > :> ... and makes the Eiffel approach more flexible (and robust: Ada raises a > :> Constraint_Error exception if the dynamic types of multiple controlling > :> operands differ RM95 3.9.2 (16) ). > : > :Not if you are using classwide types and operations. You can't even > :_have_ multiple controlling parameters in Eiffel so it is certainly no > :more "flexible" here. > Nor would we want to. One operand, Current, is more than adequate to > convey the information in accordance with the OO principle of > uniqueness. Just like I said, Eiffel is less flexible here (but not in some ground shaking way.) > In Ada, with the second, third, fourth, ... and nth > operands of the same specific type controlling dispatching, by the > time the compiler gets to the end of the routine signature, it is > rolling it's eyes and crying: "Good grief, I heard you the first > time!!!" Don, are you really this clueless or are you just being facetious? The reason I say this is that the above doesn't even work as a joke, because it is rooted in an error (instead of a clever observation). >["catcalls" and system validity check problems...] > I suspect it's only a problem in Eiffel because other languages > don't provide that flexibility. Obviously, if Eiffel were more > restrictive, it would not be Actually, Eiffel is (clearly) more restrictive here. It _tries_ to have _less_ things ensured at runtime. And the reason it is a problem is that Eiffel source does not have as much semantic information in it in this area. Ada OTOH, does and has no problem here at all. There was a rather good discussion of this stuff about a year ago. Maybe I can dig that up. /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] 218+ messages in thread
* Re: Real OO 1996-04-17 0:00 ` Jon S Anthony @ 1996-04-19 0:00 ` Don Harrison 1996-04-19 0:00 ` Jon S Anthony ` (8 more replies) 0 siblings, 9 replies; 218+ messages in thread From: Don Harrison @ 1996-04-19 0:00 UTC (permalink / raw) Jon S Anthony writes: :In article <DpqJKL.76E@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: : :> Jon S Anthony writes: :> :> :In article <DpBsG4.MIE@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: :>... :> :I think you are confused here. Don't start thinking that Eiffel's :> :"frozen" feature is the equivalent of a classwide operation. It is :> :not. Classwide operations are more general and flexible and cover :> :other sorts of ground beyond what you have with simple frozen features. :> :> Yes, Eiffel's frozen routines alone are not the equivalent of Ada's classwide :> operations. The Eiffel analogue is a combination of: :> :> a) frozen routines, which make a routine classwide for Current, and :> b) parameters, all of which are classwide by default, and :> c) results of functions, which are classwide by default. :> :> This provides the same generality and flexibility of Ada's classwide :> operations. : :I don't see how. I think the examples which I listed speak for themselves. Do you dispute that any are semantically equivalent? If so, which ones and why? I assume you are talking about 4. (See below). : For one thing clients can't define such things :without introducing gratuitous/extraneous classes (I would say this is :one of the more important uses). Not sure what you mean here. Classes T and U are not extraneous and no more classes need to be defined. : For another, you need some :clairvoyance when designing a class in determining the set of features :to freeze in order to use them in these sorts of combinations :elsewhere. This is no different from Ada. : This is a real "no no" ala C++ "virtual" tagging. From :what I can tell, Eiffel just plain does not have the equivalent :flexible capability here. Again, class wide types and their :operations are more like Sather abstract types and ops on them. :Eiffel does not have these... I'm sure you're right. I wouldn't know. :> equivalent operations in Eiffel and Ada. They are dispatching wrt :> some parameters and classwide wrt others (eg. 2. below). :>... Reinserting my example 4. 4. function i (a: T'Class) return T'Class frozen i: like Current :Hmmm, actually as I understand the semantics of Like Current, I would :say that in general these examples are _not_ equivalent. Like Current :seems to require the type of the actual/result must be the same as the :type of the specific object on which the particular invocation has :been made. No such restriction is true in the Ada case - the result :can be any type in the tree rooted at T. : :Actually, this is a little fuzzy, since there is some other talk in :ETL where I think you could read Like Current as allowing for anything :_conformant_ to the type of the object on which the invocation has :been made. Anyone know the real scoop?? If I understand Jacob correctly, the anchored entity conforms both statically and dynamically to Current. On that basis, I would say that all the examples I gave are equivalent. :> :> ... and makes the Eiffel approach more flexible (and robust: Ada raises a :> :> Constraint_Error exception if the dynamic types of multiple controlling :> :> operands differ RM95 3.9.2 (16) ). :> : :> :Not if you are using classwide types and operations. You can't even :> :_have_ multiple controlling parameters in Eiffel so it is certainly no :> :more "flexible" here. : :> Nor would we want to. One operand, Current, is more than adequate to :> convey the information in accordance with the OO principle of :> uniqueness. My point here about uniqueness is a minor one but valid nevertheless. However, I'm not about to lose sleep if you disagree. I guess the Eiffel equivalent to Ada's multiple controlling operands is something like (assuming the same definitions): 5. function k (a: T; b: T) return T is k (b: like Current): like Current is begin require ... Current.conforms_to (b) end do ... ensure Current.conforms_to (Result) end A little more long-winded but notice the semantics is probably really closer to what you want because the test on the dynamic type of Result is made after the routine body: it's nonsense to make assertions about the Result until it exists. If either the precondition or the postcondition is not met, an exception is raised, as in Ada. :Just like I said, Eiffel is less flexible here (but not in some ground :shaking way.) I hope you will agree by now that Eiffel and Ada are as flexible as each other in terms of the mechanisms they offer. ::> In Ada, with the second, third, fourth, ... and nth :> operands of the same specific type controlling dispatching, by the :> time the compiler gets to the end of the routine signature, it is :> rolling it's eyes and crying: "Good grief, I heard you the first :> time!!!" : :Don, are you really this clueless or are you just being facetious? :The reason I say this is that the above doesn't even work as a joke, :because it is rooted in an error (instead of a clever observation). Mostly facetious. But I do find it slightly ridiculous to say that there are multiple controlling operands if it only works if they happen to have the same dynamic type. (No, I'm not saying that it should be possible for them to be different). The fact that you get an exception if the types differ is not as plain as in the Eiffel paradigm, IMO. Try to quote a little of what I say. Reinserting what I wrote: You are referring to catcalls: calls which fail because a descendant class makes changes which render the call non-polymorphic wrt ancestors. The changes may be: a) a type has been redefined to a non-conformant type, or b) a routine has been made inaccessible due to a change in it's export status. At first sight, it seems silly to permit such things if they can cause problems with polymorphism but there is a worthy motivation for providing such flexibility: allowing reuse of legacy code that does not exactly fit your requirements. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :>["catcalls" and system validity check problems...] :> I suspect it's only a problem in Eiffel because other languages :> don't provide that flexibility. Obviously, if Eiffel were more :> restrictive, it would not be a problem. But flexibility and reuse are regarded as paramount, so the issue arises. It's an acknowledged problem which is currently being addressed but I don't know how. Hopefully, the solution will retain flexibility and efficiently prevent such calls being made. :Actually, Eiffel is (clearly) more restrictive here. Not at all. The problem is that it is more permissive in this area (for the good reason of allowing reuse of legacy code) and this can cause runtime errors. If Ada were just as permissive, it would be a problem for Ada too. In Ada, you can't reuse such code that almost meets your needs; you have to rewrite it which could be a formidable task. : It _tries_ to :have _less_ things ensured at runtime. The intended purpose of system-level validity checks is to identify and exclude at compile time the stuff that would cause runtime errors. If you ensure something at compile time, you also ensure it for runtime. (Yes, I know that few, if any, Eiffel vendors implement it). Don't know what you mean here. : And the reason it is a problem :is that Eiffel source does not have as much semantic information in it :in this area. What might that be? : Ada OTOH, does and has no problem here at all. There :was a rather good discussion of this stuff about a year ago. Maybe I :can dig that up. Please do. : :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Don Harrison @ 1996-04-19 0:00 ` Jon S Anthony 1996-04-23 0:00 ` Don Harrison 1996-04-19 0:00 ` Multiple Dispatch in Ada 95 (Was Re: Real OO) Brian Rogoff ` (7 subsequent siblings) 8 siblings, 1 reply; 218+ messages in thread From: Jon S Anthony @ 1996-04-19 0:00 UTC (permalink / raw) In article <Dq3EtF.8vC@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > :> Yes, Eiffel's frozen routines alone are not the equivalent of Ada's classwide > :> operations. The Eiffel analogue is a combination of: > :> > :> a) frozen routines, which make a routine classwide for Current, and > :> b) parameters, all of which are classwide by default, and > :> c) results of functions, which are classwide by default. > :> > :> This provides the same generality and flexibility of Ada's classwide > :> operations. > : > :I don't see how. > > I think the examples which I listed speak for themselves. Do you dispute that No, here I'm talking about the points I made in this paragraph, not the table stuff you gave. > : For one thing clients can't define such things > :without introducing gratuitous/extraneous classes (I would say this is > :one of the more important uses). > > Not sure what you mean here. Classes T and U are not extraneous and no more > classes need to be defined. Simply this: Classwide operations can be defined anywhere by anyone. They do not have to be defined along with the primitive types in their packages. So, suppose I have my T and U types (and classes) as you gave in your table. Long after the types/classes have been defined I am writing a client and decide that I need a new operation which is general to these types and a new one that I've just defined, S. It has signature, procedure Op (a: T'Class; b,c: U'Class; d: S'Class); In Ada, I can define this in my specific application as I see fit. Now, in Eiffel you need something like, in Class T in U in S frozen Op ( frozen Op( frozen Op( b:U,c:U,d:S) a:T,c:U,d:S) a:T,c:U,b:U) But this would mean changing the class definitions for all three classes. Even with some clairvoyance you'd have trouble as you couldn't know about S when T and U were defined. So, one possible out would be to subclass them all and put the new frozen features in these new "extraneous" classes and use them instead. > : For another, you need some > :clairvoyance when designing a class in determining the set of features > :to freeze in order to use them in these sorts of combinations > :elsewhere. > > This is no different from Ada. See the discussion above for why this is not true. > :> equivalent operations in Eiffel and Ada. They are dispatching wrt > :> some parameters and classwide wrt others (eg. 2. below). > :>... >... > :Actually, this is a little fuzzy, since there is some other talk in > :ETL where I think you could read Like Current as allowing for anything > :_conformant_ to the type of the object on which the invocation has > :been made. Anyone know the real scoop?? > > If I understand Jacob correctly, the anchored entity conforms both statically > and dynamically to Current. On that basis, I would say that all the examples > I gave are equivalent. I saw his post too and it did seem to confirm that the semantics do mean _conformant_. So, I agree they seem to be functionally equivalent. > [dispatching syntax stuff again] > I'm not about to lose sleep if you disagree. Check. > I guess the Eiffel equivalent to > Ada's multiple controlling operands is something like (assuming the same > definitions): > > function k (a: T; b: T) return T is k (b: like Current): like Current is > begin require > ... Current.conforms_to (b) > end do > ... > ensure > Current.conforms_to (Result) > end > > A little more long-winded but notice the semantics is probably really closer > to what you want because the test on the dynamic type of Result is made after > the routine body: it's nonsense to make assertions about the Result until it This is true in the Ada case as well. The body is statically checked to ensure that the result is T, of course, but during a runtime dispatch the actual result is checked to ensure its dynamic type conforms to its context. So, Ada loses nothing here. > I hope you will agree by now that Eiffel and Ada are as flexible as each other > in terms of the mechanisms they offer. I'm not sure about that. Maybe part of the issue is that "flexible" is not the appropriate term for this discussion... >...[multiple controlling params again...] > Mostly facetious. But I do find it slightly ridiculous to say that there are > multiple controlling operands if it only works if they happen to have the same > dynamic type. I wouldn't say ridiculous as there are some nice aspects you get even at this level. How about, less flexible ;-) > (No, I'm not saying that it should be possible for them to be > different). Actually, I'm not so sure about this. I understand why this limitation was made, but certainly CLOS and Dylan folk would say this _should_ be possible. And they have a point too. > The fact that you get an exception if the types differ is not as > plain as in the Eiffel paradigm, IMO. OK, but I don't see this. > You are referring to catcalls: calls which fail because a descendant class The important point is that these can't be checked at compile time (well, at least not in Eiffel 3), but that the rules require the checks be made _before_ accepting the _system_ for execution. When I say less flexible here, I just mean that _less_ cases can get through to runtime. This _can_ be a _good_ thing. >a) a type has been redefined to a non-conformant type, or >b) a routine has been made inaccessible due to a change in it's export status. > >At first sight, it seems silly to permit such things if they can >cause problems with polymorphism but there is a worthy motivation for >providing such flexibility: > >allowing reuse of legacy code that does not exactly fit your requirements. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I don't quite follow this. It does not seem to have much of anything to do with the problem of system validity checks. I suppose you can claim that overiding export policies and making covariant routine signatures (I think you mean conforming, not non-conforming) is necessary to the emphasized goal, but I don't see it. This goal is trivial to achieve in Ada, do in large part to separation of specifications (interfaces) and implementations (yeah, that old argument). Add to this separation of interface and implementation inheritance, and these problems disappear. > the good reason of allowing reuse of legacy code) and this can cause runtime > errors. If Ada were just as permissive, it would be a problem for Ada too. > In Ada, you can't reuse such code that almost meets your needs; you have to > rewrite it which could be a formidable task. But this is _trivial_ to achieve in Ada without resorting to system validity checks. I don't know why you make such an odd claim. > The intended purpose of system-level validity checks is to identify > and exclude at compile time the stuff that would cause runtime > errors. This is just plain wrong. System validity checks don't happen at compile time, but rather at binding/link time ("long" after the source has been compiled). > for runtime. (Yes, I know that few, if any, Eiffel vendors implement > it). Don't know what you mean here. I mean simply that invalid Eiffel systems are knowingly accepted by these implementations. > : And the reason it is a problem > :is that Eiffel source does not have as much semantic information in it > :in this area. > > What might that be? Specifically, the distinction and use of the difference between class and type. The funny thing is, all this brouhaha about system validity concerns this distinction and an attempt to try to separate them again. Meyer's "dynamic type set" is pretty much an Ada class and his "dynamic class set" is pretty much a class-wide type. > :was a rather good discussion of this stuff about a year ago. Maybe I > :can dig that up. > > Please do. I will try to look it up. If Robb Nebbe is reading, maybe he has a ready and complete "transcript". He was a major participant. /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] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Jon S Anthony @ 1996-04-23 0:00 ` Don Harrison 1996-04-24 0:00 ` Don Harrison ` (3 more replies) 0 siblings, 4 replies; 218+ messages in thread From: Don Harrison @ 1996-04-23 0:00 UTC (permalink / raw) Jon S Anthony writes: :In article <Dq3EtF.8vC@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: : :> :> Yes, Eiffel's frozen routines alone are not the equivalent of Ada's classwide :> :> operations. The Eiffel analogue is a combination of: :> :> :> :> a) frozen routines, which make a routine classwide for Current, and :> :> b) parameters, all of which are classwide by default, and :> :> c) results of functions, which are classwide by default. :> :> :> :> This provides the same generality and flexibility of Ada's classwide :> :> operations. :> : :> :I don't see how. :> :> I think the examples which I listed speak for themselves. Do you dispute that : :No, here I'm talking about the points I made in this paragraph, :not the table stuff you gave. Okay. :> : For one thing clients can't define such things :> :without introducing gratuitous/extraneous classes (I would say this is :> :one of the more important uses). :> :> Not sure what you mean here. Classes T and U are not extraneous and no more :> classes need to be defined. : :Simply this: Classwide operations can be defined anywhere by anyone. :They do not have to be defined along with the primitive types in :their packages. In OO software, you would want to define them with their types (except that in Eiffel, you assign the operation to a particular type - whichever is most appropriate). Otherwise, you would end up with spaghetti, IMO. : So, suppose I have my T and U types (and classes) :as you gave in your table. Long after the types/classes have been :defined I am writing a client and decide that I need a new operation :which is general to these types and a new one that I've just defined, :S. It has signature, : :procedure Op (a: T'Class; b,c: U'Class; d: S'Class); : :In Ada, I can define this in my specific application as I see fit. : :Now, in Eiffel you need something like, : :in Class T in U in S :frozen Op ( frozen Op( frozen Op( : b:U,c:U,d:S) a:T,c:U,d:S) a:T,c:U,b:U) Except that you would only need one of them - not three. Note that in any one of these, each formal parameter is effectively classwide, including Current. So, you only need one of these, as you would in Ada. :But this would mean changing the class definitions for all three :classes. : Even with some clairvoyance you'd have trouble as you :couldn't know about S when T and U were defined. So, one possible :out would be to subclass them all and put the new frozen features :in these new "extraneous" classes and use them instead. Hopefully, these comments are no longer relevant. :> : For another, you need some :> :clairvoyance when designing a class in determining the set of features :> :to freeze in order to use them in these sorts of combinations :> :elsewhere. :> :> This is no different from Ada. : :See the discussion above for why this is not true. Ditto. [In here, we agreed on something, even if it was only to diasagree :-)] :> I guess the Eiffel equivalent to :> Ada's multiple controlling operands is something like (assuming the same :> definitions): :> :> function k (a: T; b: T) return T is k (b: like Current): like Current is :> begin require :> ... Current.conforms_to (b) :> end do :> ... :> ensure :> Current.conforms_to (Result) :> end :> :> A little more long-winded but notice the semantics is probably really closer :> to what you want because the test on the dynamic type of Result is made after :> the routine body: it's nonsense to make assertions about the Result until it : :This is true in the Ada case as well. The body is statically checked to :ensure that the result is T, of course, but during a runtime dispatch :the actual result is checked to ensure its dynamic type conforms to :its context. So, Ada loses nothing here. Okay. :> I hope you will agree by now that Eiffel and Ada are as flexible as each other :> in terms of the mechanisms they offer. : :I'm not sure about that. Maybe part of the issue is that "flexible" :is not the appropriate term for this discussion... Not sure what are you still unsure about. :>...[multiple controlling params again...] :> Mostly facetious. But I do find it slightly ridiculous to say that there are :> multiple controlling operands if it only works if they happen to have the same :> dynamic type. : :I wouldn't say ridiculous as there are some nice aspects you get even :at this level. How about, less flexible ;-) Okay. :> (No, I'm not saying that it should be possible for them to be :> different). : :Actually, I'm not so sure about this. I understand why this limitation :was made, but certainly CLOS and Dylan folk would say this _should_ be :possible. And they have a point too. Is this what is meant by multiple dispatching? :> The fact that you get an exception if the types differ is not as :> plain as in the Eiffel paradigm, IMO. : :OK, but I don't see this. Only because it is explicitly stated. Nothing to get too excited over, though. :> You are referring to catcalls: calls which fail because a descendant class : :The important point is that these can't be checked at compile time :(well, at least not in Eiffel 3), but that the rules require the :checks be made _before_ accepting the _system_ for execution. When I :say less flexible here, I just mean that _less_ cases can get through :to runtime. This _can_ be a _good_ thing. : : :>a) a type has been redefined to a non-conformant type, or :>b) a routine has been made inaccessible due to a change in it's export status. :> :>At first sight, it seems silly to permit such things if they can :>cause problems with polymorphism but there is a worthy motivation for :>providing such flexibility: :> :>allowing reuse of legacy code that does not exactly fit your requirements. :^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ : :I don't quite follow this. It does not seem to have much of anything :to do with the problem of system validity checks. I suppose you can :claim that overiding export policies and making covariant routine :signatures (I think you mean conforming, not non-conforming) is :is necessary to the emphasized goal, but I don't see it. Yes, I was wrong. It should be conforming signatures, as you say. (I was trying to avoid looking it up). A good example illustrating both a) and b) is in ETL, 22.6 - Notes on the type policy. Suppose you have: class DRIVER ... end class CAR_DRIVER inherit DRIVER ... end class TRUCK_DRIVER inherit DRIVER ... end and class VEHICLE feature register_driver (d: DRIVER) is ... renew_rego_by_mail is ... ... end class CAR inherit VEHICLE ... feature register_driver (d: CAR_DRIVER) is ... ... end class TRUCK inherit VEHICLE export {NONE} renew_rego_by_mail -- trucks must be inspected ... feature register_driver (d: TRUCK_DRIVER) is ... renew_rego_by_inspection is ... ... end Example of a) ------------- If, in a client, you have: d: DRIVER cd: CAR_DRIVER v: VEHICLE t: TRUCK ... d := cd v := t v.register_driver (d) -- catcall: class valid but not system valid Example of b) ------------- In the same client: v.renew_rego_by_mail -- catcall: class valid but not system valid : This goal is :trivial to achieve in Ada, do in large part to separation of :specifications (interfaces) and implementations (yeah, that old :argument). Add to this separation of interface and implementation :inheritance, and these problems disappear. Can you explain how these avoid the problem? :> the good reason of allowing reuse of legacy code) and this can cause runtime :> errors. If Ada were just as permissive, it would be a problem for Ada too. :> In Ada, you can't reuse such code that almost meets your needs; you have to :> rewrite it which could be a formidable task. : :But this is _trivial_ to achieve in Ada without resorting to system validity :checks. I don't know why you make such an odd claim. Probably because it is more of a deduction rather than actually knowing the reason whether or why it isn't a problem in Ada. However, making such deductions is not wise, I admit :-). :> The intended purpose of system-level validity checks is to identify :> and exclude at compile time the stuff that would cause runtime :> errors. : :This is just plain wrong. System validity checks don't happen at :compile time, but rather at binding/link time ("long" after the source :has been compiled). Okay, I should have said 'statically' rather than 'at compile time'. From ETL, 18.10 - Creation validity (system level): "System-level validity, as all other validity properties, is a *static* requirement". :> for runtime. (Yes, I know that few, if any, Eiffel vendors implement :> it). Don't know what you mean here. : :I mean simply that invalid Eiffel systems are knowingly accepted by :these implementations. Correct. :> : And the reason it is a problem :> :is that Eiffel source does not have as much semantic information in it :> :in this area. :> :> What might that be? : :Specifically, the distinction and use of the difference between class :and type. The funny thing is, all this brouhaha about system validity :concerns this distinction and an attempt to try to separate them :again. No. This is not why. It's due to the distinction between static and dynamic types. : Meyer's "dynamic type set" is pretty much an Ada class and his :"dynamic class set" is pretty much a class-wide type. No. There is not really an equivalent concept in Ada because it does not support generic tagged types (as far as I'm aware) or anchored types. The dynamic type set is the set of all possible dynamic types whereas the dynamic class set is the set of all classes these are derived from. A generic class in the dynamic class set becomes numerous types in the dynamic type set. :> :was a rather good discussion of this stuff about a year ago. Maybe I :> :can dig that up. :> :> Please do. : :I will try to look it up. If Robb Nebbe is reading, maybe he has a ready :and complete "transcript". He was a major participant. : :/Jon : :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-23 0:00 ` Don Harrison @ 1996-04-24 0:00 ` Don Harrison 1996-04-29 0:00 ` Jon S Anthony ` (2 subsequent siblings) 3 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-04-24 0:00 UTC (permalink / raw) I wrote: :Jon S Anthony writes: : ::In article <Dq3EtF.8vC@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: [...] ::> : And the reason it is a problem ::> :is that Eiffel source does not have as much semantic information in it ::> :in this area. ::> ::> What might that be? :: ::Specifically, the distinction and use of the difference between class ::and type. The funny thing is, all this brouhaha about system validity ::concerns this distinction and an attempt to try to separate them ::again. : :No. This is not why. It's due to the distinction between static and dynamic :types. Correction. In the case of catcalls resulting from a change of routine signature, the problem arises when calls are attempted using a combination of objects which have incompatible dynamic types wrt the call. eg. surgeon.cut (carving_knife), when you really want surgeon.cut (scalpel) Most patients would prefer that the former is excluded :-). Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-23 0:00 ` Don Harrison 1996-04-24 0:00 ` Don Harrison @ 1996-04-29 0:00 ` Jon S Anthony 1996-04-30 0:00 ` Robert Dewar 1996-05-03 0:00 ` Don Harrison 1996-04-30 0:00 ` Joachim Durchholz 1996-04-30 0:00 ` Jon S Anthony 3 siblings, 2 replies; 218+ messages in thread From: Jon S Anthony @ 1996-04-29 0:00 UTC (permalink / raw) In article <DqAyLG.D47@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > :Simply this: Classwide operations can be defined anywhere by anyone. > :They do not have to be defined along with the primitive types in > :their packages. > In OO software, you would want to define them with their types > (except that in Eiffel, you assign the operation to a particular > type - whichever is most appropriate). Otherwise, you would end up > with spaghetti, IMO. Hmmm, not much of an argument. If you really want spaghetti, just use MI. First, classwide operations are not primitive operations and so do not constitute the "definition" of the type. So, even from a s called "purest" view there's nothing wrong with putting them elsewhere. Second, when they have more than one parameter (the typical case by far) which type/calss would they be "assigned" to? Makes no sense and is a very artificial constraint. Third, you do this sort of thing in Eiffel as well: anytime you are not "just" defining a type (for example, the "main" routine), you just have to use a contrived class. > :procedure Op (a: T'Class; b,c: U'Class; d: S'Class); > : > :In Ada, I can define this in my specific application as I see fit. > : > :Now, in Eiffel you need something like, > : > :in Class T in U in S > :frozen Op ( frozen Op( frozen Op( > : b:U,c:U,d:S) a:T,c:U,d:S) a:T,c:U,b:U) > > Except that you would only need one of them - not three. Note that in any one > of these, each formal parameter is effectively classwide, including Current. > So, you only need one of these, as you would in Ada. Yes, that's true. > : Even with some clairvoyance you'd have trouble as you > :couldn't know about S when T and U were defined. So, one possible > :out would be to subclass them all and put the new frozen features > :in these new "extraneous" classes and use them instead. > > Hopefully, these comments are no longer relevant. No, these comments are still relevant. First, clients can't define them. Second, you still need to be clairvoyant (define the operation in one of the classes - even if it is application specific) or introduce contrived/extraneous classes. Another way to look at this is that classwide operations allow you to take arbitrary universal views of things - without having to go back and inappropriately fudge the class definitions. > [In here, we agreed on something, even if it was only to diasagree :-)] Actually, we really did agree - thanks to the conformance semantics of anchored types. > :This is true in the Ada case as well. The body is statically checked to > :ensure that the result is T, of course, but during a runtime dispatch > :the actual result is checked to ensure its dynamic type conforms to > :its context. So, Ada loses nothing here. > > Okay. Hey, we agree again! ;^) >[multiple dispatch stuff...] > :> (No, I'm not saying that it should be possible for them to be > :> different). > : > :Actually, I'm not so sure about this. I understand why this limitation > :was made, but certainly CLOS and Dylan folk would say this _should_ be > :possible. And they have a point too. > > Is this what is meant by multiple dispatching? Yes.(tm/Kosh) > :> The fact that you get an exception if the types differ is not as > :> plain as in the Eiffel paradigm, IMO. > : > :OK, but I don't see this. > > Only because it is explicitly stated. Nothing to get too excited > over, though. [scratches head] - Nope, still don't see it. But I infer it's not a bit deal... /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] 218+ messages in thread
* Re: Real OO 1996-04-29 0:00 ` Jon S Anthony @ 1996-04-30 0:00 ` Robert Dewar 1996-04-30 0:00 ` Amit Patel ` (4 more replies) 1996-05-03 0:00 ` Don Harrison 1 sibling, 5 replies; 218+ messages in thread From: Robert Dewar @ 1996-04-30 0:00 UTC (permalink / raw) Don Harrison said > :Simply this: Classwide operations can be defined anywhere by anyone. > :They do not have to be defined along with the primitive types in > :their packages. > In OO software, you would want to define them with their types > (except that in Eiffel, you assign the operation to a particular > type - whichever is most appropriate). Otherwise, you would end up > with spaghetti, IMO. It's always surprising how people get carried away with one narrow view of programming paradigms. It very much reminds me of the old saying that "if the only tool you have is a hammer, then all problems start to resemble nails". The idea that you should package operations with types is a very useful (and very old one). The particular enbodiment that we refer to as object oriented programming is in particular a very useful (and also very old) idea. But going one step further and saying that this idea is so effective that ALL software must be organized this way, and that any other organization is defective is simply not justified. As I say, this seems a common affliction in the programming languages field: o Functional programming is very useful, but disallowing notions of implicit state completely seems to be going much too far. o Proof of correctness is very useful, but insisting that programming languages be designed so that only programs with integral proofs can be written seems to be going much too far. o Strong typing is very useful, but insisting that separate types be used for every separate notion, no matter on how small a scale, is going too far. o Top-down structured design is very useful, but insisting that ALL programs use only this design approach is going too far. etc. To me the "pure" OO view that Don expresses is yet another example of this excess of zeal. There are many instances in which it makes sense to package operations by classifying operations, rather than types. For example, It seems quite appropriate to have a set of procedures for matrix diagonalization packaged up together, rather than bundled in with the matrix type (indeed the idea of bundling all possible matrix operatoins up with the matrix type is untenable). Similarly, it makes sense to have trig functions for various floating-point types bundled up in a package, rather than scattered around. The world of operations and types can be though of as a matrix: type 1 type 2 type 3 ... op1 x - x op2 - x - op3 x x - ... Roughtly speaking, the OO approach organizes by columns, which is often fine, but the operation organization by rows also often makes sense. For example, if op3 represents some complex numerical procedures, understood only by a small group of experts, it may well be better to bundle up op3 rather than distributing the op3 code around the separate types. A maximally effective programming language will be friendly to either the "row" view or the "column" view, or flexible mixtures of the two. P.S. spaghetti code doesn't just mean code you don't like. It is a rather specific term used to described the non-nested chaotic control structures caused by undisciplined use of gotos or equivalent transfers of control. I think it is useful to keep this sense. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert Dewar @ 1996-04-30 0:00 ` Amit Patel 1996-04-30 0:00 ` Robert A Duff 1996-05-01 0:00 ` Norman H. Cohen 1996-04-30 0:00 ` Robert A Duff ` (3 subsequent siblings) 4 siblings, 2 replies; 218+ messages in thread From: Amit Patel @ 1996-04-30 0:00 UTC (permalink / raw) In article <dewar.830866793@schonberg>, Robert Dewar <dewar@cs.nyu.edu> wrote: [lots of good stuff deleted] >There are many instances in which it makes sense to package operations >by classifying operations, rather than types. For example, It seems quite >appropriate to have a set of procedures for matrix diagonalization packaged >up together, rather than bundled in with the matrix type (indeed the idea >of bundling all possible matrix operatoins up with the matrix type is >untenable). Similarly, it makes sense to have trig functions for various >floating-point types bundled up in a package, rather than scattered around. > >The world of operations and types can be though of as a matrix: > > type 1 type 2 type 3 ... > > op1 x - x > op2 - x - > op3 x x - > ... > >Roughtly speaking, the OO approach organizes by columns, which is often >fine, but the operation organization by rows also often makes sense. >For example, if op3 represents some complex numerical procedures, >understood only by a small group of experts, it may well be better >to bundle up op3 rather than distributing the op3 code around the >separate types. The row organization seems to correspond to "tagged unions" as in ML, or less safe structures like unions in C or variant records in Pascal. There are a fixed number of data variants, and you want to be able to write lots of code on them. Another example is a compiler, which has a fixed set of intermediate representation data variants (add, mul, mov, etc.) and a varying number of operations (code optimization passes). Each optimization pass can be put into a separate module, and new optimization passes can be added without modifying the intermediate code definition. If one tried to implement this with objects, then each optimization pass would be a method on the object. To add a new optimization, you have to modify *every* data variant. >A maximally effective programming language will be friendly to either the >"row" view or the "column" view, or flexible mixtures of the two. Agreed. - Amit ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Amit Patel @ 1996-04-30 0:00 ` Robert A Duff 1996-05-07 0:00 ` Amit Patel 1996-05-01 0:00 ` Norman H. Cohen 1 sibling, 1 reply; 218+ messages in thread From: Robert A Duff @ 1996-04-30 0:00 UTC (permalink / raw) In article <4m5ugh$ben@nntp.Stanford.EDU>, Amit Patel <amitp@Xenon.Stanford.EDU> wrote: >Another example is a compiler, which has a fixed set of intermediate >representation data variants (add, mul, mov, etc.) and a varying >number of operations (code optimization passes). Each optimization >pass can be put into a separate module, and new optimization passes >can be added without modifying the intermediate code definition. What if the "representation data variants" are *not* fixed. How do you design things so that it's easy to add a new one of those, and *also* easy to add a new optimization pass? "Design Patterns" by Gamma et al seems to force you to decide which of those two dimensions you might want to modify in the future, and design accordingly. But what if *both* are likely to need modification? - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert A Duff @ 1996-05-07 0:00 ` Amit Patel 0 siblings, 0 replies; 218+ messages in thread From: Amit Patel @ 1996-05-07 0:00 UTC (permalink / raw) Robert A Duff <bobduff@world.std.com> wrote: >In article <4m5ugh$ben@nntp.Stanford.EDU>, >Amit Patel <amitp@Xenon.Stanford.EDU> wrote: >>Another example is a compiler, which has a fixed set of intermediate >>representation data variants (add, mul, mov, etc.) and a varying >>number of operations (code optimization passes). Each optimization >>pass can be put into a separate module, and new optimization passes >>can be added without modifying the intermediate code definition. > >What if the "representation data variants" are *not* fixed. How do you >design things so that it's easy to add a new one of those, and *also* >easy to add a new optimization pass? "Design Patterns" by Gamma et al >seems to force you to decide which of those two dimensions you might >want to modify in the future, and design accordingly. But what if >*both* are likely to need modification? If both really need to be modified, there's a problem! How are old optimization passes supposed to work with new data, and how to do new optimization passes work with old data? If you're extending in two dimensions, it's very hard to take two extensions and combine them. For example, the original compiler has two data variants: A and B. There are two optimization passes, 1 and 2. Bob extends the system to have a new data variant, C. He may have to modify 1 and 2 to work with C, if union types are used. Therefore, Bob thinks it's better to use objects. Amit extends the system to have a new optimization pass, 3. He may have to modify A and B to work with 3, if object types are used. Therefore, Amit thinks it's better to use union types. Multimethods provide a partial solution to the problem. You can add new data variants, AND define new operations on existing data variants. The problem, though, is combining Amit's work with Bob's work. Amit shouldn't be expected to make 3 work with C, because he didn't know about C. Bob shouldn't be expected to make C work with 3, because he didn't know about 3. The case still has to be defined, though. So if you choose single-dispatch objects, you can extend the system with new data variants. You can combine extensions from different places and have the system work. If you choose tagged unions, you can extend the system with new code. You can combine extensions from different places and have the system work. If you choose multimethods, you can extend the system with data variants. You can extend the system with new code. You can't expect extensions from different sources to work (in general). It seems like you get two out of three! - Amit ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Amit Patel 1996-04-30 0:00 ` Robert A Duff @ 1996-05-01 0:00 ` Norman H. Cohen 1996-05-01 0:00 ` Colin James III (The Rt Rev'd) 1996-05-07 0:00 ` Amit Patel 1 sibling, 2 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-05-01 0:00 UTC (permalink / raw) In article <4m5ugh$ben@nntp.Stanford.EDU>, amitp@Xenon.Stanford.EDU (Amit Patel) writes: |> Another example is a compiler, which has a fixed set of intermediate |> representation data variants (add, mul, mov, etc.) and a varying |> number of operations (code optimization passes). Each optimization |> pass can be put into a separate module, and new optimization passes |> can be added without modifying the intermediate code definition. |> |> If one tried to implement this with objects, then each optimization |> pass would be a method on the object. To add a new optimization, you |> have to modify *every* data variant. An optmization is an operation on a sequence of instructions, not on an instruction. It makes no sense to apply an Optimize operation to an ADD instruction. Rather, if there were an abstract type for instructions, with subclasses for various categories of instructions, there would be a set of operations defined for all kinds of instructions, such as: function Operand_Count (Instruction: Instruction_Type) return Natural; function Operand (Instruction: Instruction_Type; Position: Positive) return Operand_Type; function New_Instruction (Opcode: Opcode_Type; Operands: Operand_List_Type) return Instruction_Type'Class; function Is_Associative (Instruction: Instruction_Type) return Boolean; ... Operand_Type would also be a class, with subclasses for general registers, floating-point registers, real immediate values, integer immediate values, and so forth. (There would be a dispatching Image function defined for Operand_Type, useful for producing diagnostic information.) Operand_List_Type would be an unconstrained array type of pointers to Operand_Type'Class objects. Instruction_Type'Class would be used in defining a type for instruction lists. Each optimization pass could be a procedure taking an instruction list as an in out parameter. It would invoke dispatching operations of instructions to manipulate instruction lists, but an optimization pass would not itself be a dispatching operation, on instructions, instruction lists, or anything else. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Norman H. Cohen @ 1996-05-01 0:00 ` Colin James III (The Rt Rev'd) 1996-05-07 0:00 ` Amit Patel 1 sibling, 0 replies; 218+ messages in thread From: Colin James III (The Rt Rev'd) @ 1996-05-01 0:00 UTC (permalink / raw) Norman Cohen at Watson Research Center of IBM is slipping again, perhaps becoming infirm. This again has nothing whatsoever to do with Eiffel, but lots to do with dead Ada. ------------------------------------------------- ncohen@watson.ibm.com (Norman H. Cohen) wrote: >In article <4m5ugh$ben@nntp.Stanford.EDU>, amitp@Xenon.Stanford.EDU >(Amit Patel) writes: > >|> Another example is a compiler, which has a fixed set of intermediate >|> representation data variants (add, mul, mov, etc.) and a varying >|> number of operations (code optimization passes). Each optimization >|> pass can be put into a separate module, and new optimization passes >|> can be added without modifying the intermediate code definition. >|> >|> If one tried to implement this with objects, then each optimization >|> pass would be a method on the object. To add a new optimization, you >|> have to modify *every* data variant. > >An optmization is an operation on a sequence of instructions, not on an >instruction. It makes no sense to apply an Optimize operation to an ADD >instruction. > >Rather, if there were an abstract type for instructions, with >subclasses for various categories of instructions, there would >be a set of operations defined for all kinds of instructions, such as: > > function Operand_Count (Instruction: Instruction_Type) return Natural; > function Operand > (Instruction: Instruction_Type; Position: Positive) > return Operand_Type; > function New_Instruction > (Opcode: Opcode_Type; Operands: Operand_List_Type) > return Instruction_Type'Class; > function Is_Associative (Instruction: Instruction_Type) return Boolean; > ... > >Operand_Type would also be a class, with subclasses for general >registers, floating-point registers, real immediate values, integer >immediate values, and so forth. (There would be a dispatching Image >function defined for Operand_Type, useful for producing diagnostic >information.) Operand_List_Type would be an unconstrained array type of >pointers to Operand_Type'Class objects. > >Instruction_Type'Class would be used in defining a type for instruction >lists. Each optimization pass could be a procedure taking an instruction >list as an in out parameter. It would invoke dispatching operations of >instructions to manipulate instruction lists, but an optimization pass >would not itself be a dispatching operation, on instructions, instruction >lists, or anything else. > >-- >Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Norman H. Cohen 1996-05-01 0:00 ` Colin James III (The Rt Rev'd) @ 1996-05-07 0:00 ` Amit Patel 1 sibling, 0 replies; 218+ messages in thread From: Amit Patel @ 1996-05-07 0:00 UTC (permalink / raw) Norman H. Cohen <ncohen@watson.ibm.com> wrote: >In article <4m5ugh$ben@nntp.Stanford.EDU>, amitp@Xenon.Stanford.EDU >(Amit Patel) writes: > >|> Another example is a compiler, which has a fixed set of intermediate >|> representation data variants (add, mul, mov, etc.) and a varying >|> number of operations (code optimization passes). Each optimization >|> pass can be put into a separate module, and new optimization passes >|> can be added without modifying the intermediate code definition. >|> >|> If one tried to implement this with objects, then each optimization >|> pass would be a method on the object. To add a new optimization, you >|> have to modify *every* data variant. > >An optmization is an operation on a sequence of instructions, not on an >instruction. It makes no sense to apply an Optimize operation to an ADD >instruction. Okay, I agree. The problem still exists, in a slightly different setting. There may be several operations on `instructions', like code generation for different platforms (assuming architectures are similar enough), or display for debugging purposes. The diagram is: Gen1 Gen2 Disp ---- ---- ---- ADD X X X SUB X X X ---------> CODE MOV X X X JMP X X X | | V DATA If you only want to add new operations, then you can use tagged unions, and you can extend in the CODE axis. If you only want to add new instructions, you can use objects, and you can extend in the DATA axis. If you pick the `wrong' solution for the problem (i.e., tagged unions when you want to add new instructions), then you get an inflexible system. Lots of OO textbooks show examples where you have to modify *every* module (each containing one operation) in your system in order to add a new data variant. What the OO books don't tell you is that the same thing is true for the other `wrong' solution: objects when you want to add new operations. If operations are methods, and each module contains one data variant, then to add a new method you must modify *every* module in your system. Unfortunately, this case is usually overlooked. The textbooks are concerned only with extending things in the DATA axis, and not the CODE axis. IMO, they should stress *analyzing* the problem to determine whether future extensibility will be primarily in the DATA or CODE axis. If it in the DATA axis, objects should be used for that data type. If it in the CODE axis, then objects should *not* be used, but rather, tagged unions should be used. I think this entire OOD revolution has gone too far in taking SOME examples where you want data flexibility and generalizing by saying that ALL cases require data flexibility. ("Everything is an object! So-and-so language is PURE!") There are cases where code flexibility is more important than data flexibility, and those cases are where the traditional solutions of having a 'tag' and a case statement make more sense. Let's not throw out everything that we did before! - Amit -- Amit J Patel, Computer Science Department, Stanford University http://www-cs-students.stanford.edu/~amitp/ "I am always ready to learn, but I do not always like to be taught." - Winston Churchill ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert Dewar 1996-04-30 0:00 ` Amit Patel @ 1996-04-30 0:00 ` Robert A Duff 1996-04-30 0:00 ` Amit Patel ` (2 more replies) 1996-05-01 0:00 ` AdaWorks ` (2 subsequent siblings) 4 siblings, 3 replies; 218+ messages in thread From: Robert A Duff @ 1996-04-30 0:00 UTC (permalink / raw) In article <dewar.830866793@schonberg>, Robert Dewar <dewar@cs.nyu.edu> wrote: >The world of operations and types can be though of as a matrix: > > type 1 type 2 type 3 ... > > op1 x - x > op2 - x - > op3 x x - > ... > >Roughtly speaking, the OO approach organizes by columns, which is often >fine, but the operation organization by rows also often makes sense. I wonder if it would be possible to design a language, or a tool, that is capable of showing both the rows and the columns, depending on which I want to look at right now, or maybe there's some way of expressing both at the same time. Usually, the by-column (OO) organization is better, but as Robert points out, not always. And sometimes, after I've got a bunch of OO-ish code, I find myself searching for all occurrences of a given "method" -- that is, sometimes I want to look at the rows. Look at "Design Pattern", by Gamma et al. There are many design patterns in there where operations are organized by Robert's rows -- in some cases, they advocate creating classes/objects that really represent those rows, collecting all operations of the same kind together, rather than attaching the operations to the classes they operate on. Of course, their examples are all in Smalltalk and C++, so they create "extra" classes/objects to represent these rows -- and conceptually, these objects are not really objects in the normal OO sense. In Ada, some of those design patterns would be written using packages rather than classes. >P.S. spaghetti code doesn't just mean code you don't like. It is a rather >specific term used to described the non-nested chaotic control structures >caused by undisciplined use of gotos or equivalent transfers of control. >I think it is useful to keep this sense. Well, words change their meaning, and get additional meanings. After all, think about an Italian chef admonishing us that "spaghetti" is really a kind of pasta, and the term shouldn't be evilly hijacked to talk about gotos. I've seen the term "spaghetti" used quite a few times to describe messy multiple inheritance patterns. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert A Duff @ 1996-04-30 0:00 ` Amit Patel 1996-04-30 0:00 ` Robert Dewar 1996-05-01 0:00 ` Adam Beneschan 2 siblings, 0 replies; 218+ messages in thread From: Amit Patel @ 1996-04-30 0:00 UTC (permalink / raw) In article <DqoLrA.27y@world.std.com>, Robert A Duff <bobduff@world.std.com> wrote: >I wonder if it would be possible to design a language, or a tool, that >is capable of showing both the rows and the columns, depending on which >I want to look at right now, or maybe there's some way of expressing >both at the same time. Usually, the by-column (OO) organization is >better, but as Robert points out, not always. And sometimes, after I've >got a bunch of OO-ish code, I find myself searching for all occurrences >of a given "method" -- that is, sometimes I want to look at the rows. The Apple Dylan environment has(had?) some way to perform a query on methods. You could query for all methods with a certain name, and that would give you the row, or you could query for all methods that work with a specific class, which would give you the column. I'm not sure what happened to Apple Dylan .. - Amit ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert A Duff 1996-04-30 0:00 ` Amit Patel @ 1996-04-30 0:00 ` Robert Dewar 1996-05-01 0:00 ` Richard Bielak 1996-05-01 0:00 ` Adam Beneschan 2 siblings, 1 reply; 218+ messages in thread From: Robert Dewar @ 1996-04-30 0:00 UTC (permalink / raw) Bob said "Well, words change their meaning, and get additional meanings. After all, think about an Italian chef admonishing us that "spaghetti" is really a kind of pasta, and the term shouldn't be evilly hijacked to talk about gotos. I've seen the term "spaghetti" used quite a few times to describe messy multiple inheritance patterns." Surely someone can come up with some colorful new image for messy code of this type. It needs an appropriate term. Calling it spaghetti coding might be unfair to spagehetti :-) ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert Dewar @ 1996-05-01 0:00 ` Richard Bielak 0 siblings, 0 replies; 218+ messages in thread From: Richard Bielak @ 1996-05-01 0:00 UTC (permalink / raw) In article <dewar.830920178@schonberg>, Robert Dewar <dewar@cs.nyu.edu> wrote: >Bob said > >"Well, words change their meaning, and get additional meanings. After >all, think about an Italian chef admonishing us that "spaghetti" is >really a kind of pasta, and the term shouldn't be evilly hijacked to >talk about gotos. I've seen the term "spaghetti" used quite a few times >to describe messy multiple inheritance patterns." > >Surely someone can come up with some colorful new image for messy >code of this type. It needs an appropriate term. Calling it spaghetti >coding might be unfair to spagehetti :-) > I've heard OO designs called "spagehetti and meatballs" since OO diagrams are collections of circles connected by arrows. Another image I like is the "plate of squid", each squid representing an object/class, 'cause if you pick one up you'll drag along whole bunch of others... ...richie -- * richieb@ritz.mordor.com - at home | Richie Bielak * * richieb@calfp.com - at work | * * >> If it were readable, it wouldn't be called "code". (me) << * *------------------------------------------------------------------* ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert A Duff 1996-04-30 0:00 ` Amit Patel 1996-04-30 0:00 ` Robert Dewar @ 1996-05-01 0:00 ` Adam Beneschan 1996-05-02 0:00 ` Ell 2 siblings, 1 reply; 218+ messages in thread From: Adam Beneschan @ 1996-05-01 0:00 UTC (permalink / raw) bobduff@world.std.com (Robert A Duff) writes: >Well, words change their meaning, and get additional meanings. After >all, think about an Italian chef admonishing us that "spaghetti" is >really a kind of pasta, and the term shouldn't be evilly hijacked to >talk about gotos. I've seen the term "spaghetti" used quite a few times >to describe messy multiple inheritance patterns. I'd just tell the chef that "hijacking" is a violent act of using a death threat to force an airplane or other transportation vehicle to be taken to a different destination; and the term shouldn't be evilly misappropriated to talk about metaphors. :) -- Adam ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Adam Beneschan @ 1996-05-02 0:00 ` Ell 0 siblings, 0 replies; 218+ messages in thread From: Ell @ 1996-05-02 0:00 UTC (permalink / raw) Adam Beneschan (adam@irvine.com) wrote: : bobduff@world.std.com (Robert A Duff) writes: : : >Well, words change their meaning, and get additional meanings. After : >all, think about an Italian chef admonishing us that "spaghetti" is : >really a kind of pasta, and the term shouldn't be evilly hijacked to : >talk about gotos. I've seen the term "spaghetti" used quite a few times : >to describe messy multiple inheritance patterns. : I'd just tell the chef that "hijacking" is a violent act of using a : death threat to force an airplane or other transportation vehicle to : be taken to a different destination; and the term shouldn't be evilly : misappropriated to talk about metaphors. Right. I prefer the SADP use of "spaghetti" as unconstrained variation of the flow of control (or control flow :-). Elliott ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert Dewar 1996-04-30 0:00 ` Amit Patel 1996-04-30 0:00 ` Robert A Duff @ 1996-05-01 0:00 ` AdaWorks 1996-05-01 0:00 ` Don Harrison 1996-05-08 0:00 ` Joachim Durchholz 4 siblings, 0 replies; 218+ messages in thread From: AdaWorks @ 1996-05-01 0:00 UTC (permalink / raw) Robert Dewar (dewar@cs.nyu.edu) wrote: [snip, snip, snip] : > In OO software, you would want to define them with their types : > (except that in Eiffel, you assign the operation to a particular : > type - whichever is most appropriate). Otherwise, you would end up : > with spaghetti, IMO. [snip, snip, snip] : P.S. spaghetti code doesn't just mean code you don't like. It is a rather : specific term used to described the non-nested chaotic control structures : caused by undisciplined use of gotos or equivalent transfers of control. : I think it is useful to keep this sense. HmmmMMMMMMmmmmm. I guess we could characterize OOP as "ravioli code" rather than spaghetti code. Each class is a little square of pasta enclosing the encapsulated delicacies. Anyone for Pizza code? Sorry about that, folks. It is late in the day and the Ada problem I a trying to solve has triggered onset delirium. :-) :-) Richard Riehle -- richard@adaworks.com AdaWorks Software Engineering Suite 27 2555 Park Boulevard Palo Alto, CA 94306 (415) 328-1815 FAX 328-1112 ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert Dewar ` (2 preceding siblings ...) 1996-05-01 0:00 ` AdaWorks @ 1996-05-01 0:00 ` Don Harrison 1996-05-01 0:00 ` David Hopwood ` (2 more replies) 1996-05-08 0:00 ` Joachim Durchholz 4 siblings, 3 replies; 218+ messages in thread From: Don Harrison @ 1996-05-01 0:00 UTC (permalink / raw) Robert Dewar writes: :Don Harrison said : :> :Simply this: Classwide operations can be defined anywhere by anyone. :> :They do not have to be defined along with the primitive types in :> :their packages. : :> In OO software, you would want to define them with their types :> (except that in Eiffel, you assign the operation to a particular :> type - whichever is most appropriate). Otherwise, you would end up :> with spaghetti, IMO. : :It's always surprising how people get carried away with one narrow view :of programming paradigms. It very much reminds me of the old saying that :"if the only tool you have is a hammer, then all problems start to :resemble nails". : :The idea that you should package operations with types is a very useful :(and very old one). The particular enbodiment that we refer to as object :oriented programming is in particular a very useful (and also very old) :idea. : :But going one step further and saying that this idea is so effective that :ALL software must be organized this way, and that any other organization :is defective is simply not justified. : :As I say, this seems a common affliction in the programming languages :field: : : o Functional programming is very useful, but disallowing notions of : implicit state completely seems to be going much too far. : : o Proof of correctness is very useful, but insisting that programming : languages be designed so that only programs with integral proofs : can be written seems to be going much too far. : : o Strong typing is very useful, but insisting that separate types be : used for every separate notion, no matter on how small a scale, is : going too far. Isn't this pure OO rather than strong typing? If you consider consistency important, it is not. : o Top-down structured design is very useful, but insisting that ALL : programs use only this design approach is going too far. : : etc. : :To me the "pure" OO view that Don expresses is yet another example of this :excess of zeal. : :There are many instances in which it makes sense to package operations :by classifying operations, rather than types. For example, It seems quite :appropriate to have a set of procedures for matrix diagonalization packaged :up together, rather than bundled in with the matrix type (indeed the idea :of bundling all possible matrix operatoins up with the matrix type is :untenable). Similarly, it makes sense to have trig functions for various :floating-point types bundled up in a package, rather than scattered around. : :The world of operations and types can be though of as a matrix: : : type 1 type 2 type 3 ... : : op1 x - x : op2 - x - : op3 x x - : ... : :Roughtly speaking, the OO approach organizes by columns, which is often :fine, but the operation organization by rows also often makes sense. :For example, if op3 represents some complex numerical procedures, :understood only by a small group of experts, it may well be better :to bundle up op3 rather than distributing the op3 code around the :separate types. : :A maximally effective programming language will be friendly to either the :"row" view or the "column" view, or flexible mixtures of the two. : :P.S. spaghetti code doesn't just mean code you don't like. It is a rather :specific term used to described the non-nested chaotic control structures :caused by undisciplined use of gotos or equivalent transfers of control. :I think it is useful to keep this sense. This may surprise you, but I actually agree with most of what you've said. I've been thinking about the symmetry issue and think that the most elegant approach may be to separate operations from the objects they act on. But, before you get excited, this doesn't mean that I think that I think that the Ada model is the right one. I think that the operations should 'float' around in operation space just as classes 'float' around in class space. What I mean is that rather than ascribing ownership of an operation to a specific class, it may be better to allow co-ownership by each class corresponding to parameters. In such a model, the parameters are all symmetric and the semantic encapsulation boundaries would overlap. The semantic boundary of a class would cease to correspond to a syntactic structure, but tools would generate a view of the class in which the semantic and syntactic structures coincide. This would appear the same as an Eiffel class. The difference is that the common operations would be duplicated in the classes that share them and all the operations (primitive and 'classwide') would appear (some filtering may be necessary). Here is an example: class A feature p: P q: Q r: R is once ... end invariant ... end class B feature s: S t: T invariant ... end Looks the same, so far. But, isn't. Now, define some operations ... do_x (a: frozen A; b: B) is -- a is 'classwide': it may not be redefined do -- b may be redefined ... end do_y (a: A; b: B) is -- multiple dispatching require a.p > 0 and b.s > 0 do ... end do_z (a: separate A; b: B) is -- concurrency: a is locked require a.p > 0 and -- synchronisation condition b.s > 0 -- correctness condition do ... end next_p (a: A): P -- function require a /= Void do Result := a.p ensure Result /= Void end do_w (a: A; b: like A) is -- anchored parameter do ... end ----------------------------------------------------------------------------- The output of the merging tool would be: Class A ------- class A feature p: P q: Q r: R is once ... end do_x (a: frozen A; b: B) is -- a is 'classwide': it may not be redefined do -- b may be redefined ... end do_y (a: A; b: B) is -- multiple dispatching require a.p > 0 and b.s > 0 do ... end do_z (a: separate A; b: B) is -- concurrency: a is locked require a.p > 0 and -- synchronisation condition b.s > 0 -- correctness condition do ... end next_p (a: A): P -- function require a /= Void do Result := a.p ensure Result /= Void end do_w (a: A; b: like A) is -- anchored parameter do ... end invariant ... end Class B ------- class B feature s: S t: T do_x (a: frozen A; b: B) is -- a is 'classwide': it may not be redefined do -- b may be redefined ... end do_y (a: A; b: B) is -- multiple dispatching require a.p > 0 and b.s > 0 do ... end do_z (a: separate A; b: B) is -- concurrency: a is locked require a.p > 0 and -- synchronisation condition b.s > 0 -- correctness condition do ... end invariant ... end Class P ------- class P feature ... next_p (a: A): P -- function require a /= Void do Result := a.p ensure Result /= Void end invariant ... end ----------------------------------------------------------------------------- Comparison with Eiffel: 1) Each parameter to an operation has the same status: there is no Current object and attributes of each parameter object may be updated directly eg. a.y := ... b.x := ... This is more permissive than Eiffel. 2) Multiple dispatching permitted. 3) Freezing occurs at finer granualarity: at the parameter level rather than the routine level. Freezing all parameters is equivalent to freezing the routine. 4) Operations must preserve the invariant of each parameter. 5) Functions redefined as attributes simply cease to appear as functions and appear as attributes. 6) Concurrency mechanism is more symmetrical. 7) Whatever other adaptations are necessary ... What have I missed? The intention is that the same safeguards as Eiffel would be maintained while offering higher modelling integrity and slightly more flexibility (direct access to all parameters and multiple dispatching FWIW). I suspect translation of Eiffel programs into this language would be quite straightforward. Would the reverse be more difficult? BTW, I'm not proposing a new language. Just playing with ideas. But, just in case you want a name, let's see ... Blackpool? No. ... Pisa? No. ... Babel? No. ... How about 'Ziggy' (short for ziggurat: the same kind of Mesopotamian tower as the tower of Babel (because it reflects an arrogant idealism and adds to the confusion of myriad programming languages)). Comments? Do you love it? Hate it? Don't care? Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Don Harrison @ 1996-05-01 0:00 ` David Hopwood 1996-05-03 0:00 ` Don Harrison 1996-05-01 0:00 ` Don Harrison 1996-05-02 0:00 ` Robert A Duff 2 siblings, 1 reply; 218+ messages in thread From: David Hopwood @ 1996-05-01 0:00 UTC (permalink / raw) You might want to look at Craig Chambers' Cecil language: http://www.cs.washington.edu/research/projects/cecil/ It has many of the features you've described for 'Ziggy'. David Hopwood david.hopwood@lmh.ox.ac.uk ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` David Hopwood @ 1996-05-03 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-03 0:00 UTC (permalink / raw) David Hopwood writes: :You might want to look at Craig Chambers' Cecil language: : : http://www.cs.washington.edu/research/projects/cecil/ : :It has many of the features you've described for 'Ziggy'. Thanks for the reference. :David Hopwood :david.hopwood@lmh.ox.ac.uk /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Don Harrison 1996-05-01 0:00 ` David Hopwood @ 1996-05-01 0:00 ` Don Harrison 1996-05-02 0:00 ` Robert A Duff 1996-05-02 0:00 ` Robert A Duff 2 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-05-01 0:00 UTC (permalink / raw) A few corrections/clarifications to my 'Ziggy' post: : ... But, before :you get excited, this doesn't mean that I think that I think that the Ada ^^^^^^^^^^^^ Stammering :-). [...] :Comparison with Eiffel: :2) Multiple dispatching permitted. Operations multiply dispatch by default. This may be reduced to single dispatching equivalent to Eiffel's by freezing all but one parameter. Note that this is different from (and more general than) Ada's 'multiple controlling operands'. [...] :6) Concurrency mechanism is more symmetrical. Because all of the operands to a separate routine are locked at the same time. [My understanding of SCOOP is that Current would be locked first (as a result of an enclosing call), then any separate parameters]. /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Ziggy ... what's Ziggy? ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Don Harrison @ 1996-05-02 0:00 ` Robert A Duff 1996-05-03 0:00 ` Don Harrison 0 siblings, 1 reply; 218+ messages in thread From: Robert A Duff @ 1996-05-02 0:00 UTC (permalink / raw) In article <Dqpuqu.IJs@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >Because all of the operands to a separate routine are locked at the same time. >[My understanding of SCOOP is that Current would be locked first (as a result of >an enclosing call), then any separate parameters]. I'll have to read up on SCOOP one of these days. I'm not surprised that it defines an order for locking. It's not clear to me how one can efficiently lock multiple objects "at the same time", i.e. as a single atomic operation. To avoid deadlocks, it seems like much complicated hand-shaking would be necessary. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Robert A Duff @ 1996-05-03 0:00 ` Don Harrison 1996-05-03 0:00 ` Robert A Duff 0 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-05-03 0:00 UTC (permalink / raw) Robert A Duff writes: :In article <Dqpuqu.IJs@assip.csasyd.oz>, :Don Harrison <donh@syd.csa.com.au> wrote: :>Because all of the operands to a separate routine are locked at the same time. :>[My understanding of SCOOP is that Current would be locked first (as a result of :>an enclosing call), then any separate parameters]. : :I'll have to read up on SCOOP one of these days. I'm not surprised that :it defines an order for locking. It's not clear to me how one can :efficiently lock multiple objects "at the same time", i.e. as a single :atomic operation. To avoid deadlocks, it seems like much complicated :hand-shaking would be necessary. To lock all parameters atomically in SCOOP, I think you have to use an extraneous wrapper class containing an operation with the same parameters. Eg, class WRAPPER feature wrapper_op (a: separate A; b: separate B) is -- locks a and b atomically do a.op (b) end end class A feature op (b: B) is ... end end In 'Ziggy', you would just write: op (a: separate A; b: separate B) is ... end -- locks a and b atomically No need for a wrapper. :- Bob /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-03 0:00 ` Don Harrison @ 1996-05-03 0:00 ` Robert A Duff 1996-05-06 0:00 ` Don Harrison 0 siblings, 1 reply; 218+ messages in thread From: Robert A Duff @ 1996-05-03 0:00 UTC (permalink / raw) In article <Dqt4BM.7up@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >In 'Ziggy', you would just write: > >op (a: separate A; b: separate B) is ... end -- locks a and b atomically > >No need for a wrapper. Sorry I wasn't clear. Yes, I understand the above. My question was, how does the person writing the Ziggy compiler/run-time system *implement* this "simultaneous" locking of a and b? At the machine-code level, I mean? - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-03 0:00 ` Robert A Duff @ 1996-05-06 0:00 ` Don Harrison 1996-05-06 0:00 ` Robb Nebbe 1996-05-06 0:00 ` Robert A Duff 0 siblings, 2 replies; 218+ messages in thread From: Don Harrison @ 1996-05-06 0:00 UTC (permalink / raw) Robert A Duff writes: :I'll have to read up on SCOOP one of these days. I'm not surprised that :it defines an order for locking. It's not clear to me how one can :efficiently lock multiple objects "at the same time", i.e. as a single :atomic operation. To avoid deadlocks, it seems like much complicated :hand-shaking would be necessary. Me: :>In 'Ziggy', you would just write: :> :>op (a: separate A; b: separate B) is ... end -- locks a and b atomically :> :>No need for a wrapper. Bob again: :Sorry I wasn't clear. Yes, I understand the above. My question was, :how does the person writing the Ziggy compiler/run-time system :*implement* this "simultaneous" locking of a and b? At the machine-code :level, I mean? There are others who could answer this better than I, but you would expect that locking would actually be sequential at the machine-code level. Could you use an OS call to exclude all other processes during locking and revoke that privilege when completed? : :- Bob /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-06 0:00 ` Don Harrison @ 1996-05-06 0:00 ` Robb Nebbe 1996-05-06 0:00 ` Robert A Duff 1 sibling, 0 replies; 218+ messages in thread From: Robb Nebbe @ 1996-05-06 0:00 UTC (permalink / raw) Don Harrison wrote: > > Robert A Duff writes: > > :Sorry I wasn't clear. Yes, I understand the above. My question was, > :how does the person writing the Ziggy compiler/run-time system > :*implement* this "simultaneous" locking of a and b? At the machine-code > :level, I mean? > > There are others who could answer this better than I, but you would expect that > locking would actually be sequential at the machine-code level. Could you use > an OS call to exclude all other processes during locking and revoke that > privilege when completed? > If you read the information that Bertrand Meyer made available at http://www.eiffel.com this particular question is glossed over (along with a lot of other questions but it is only a draft). The problem is mentioned as being hard and that it may not be implemented in the early versions. Hopefully this will not turn out like system validity which was sufficiently complicated that it was never implemented. Robb Nebbe nebbe@iam.unibe.ch ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-06 0:00 ` Don Harrison 1996-05-06 0:00 ` Robb Nebbe @ 1996-05-06 0:00 ` Robert A Duff 1 sibling, 0 replies; 218+ messages in thread From: Robert A Duff @ 1996-05-06 0:00 UTC (permalink / raw) In article <Dqyy07.529@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >There are others who could answer this better than I, but you would expect that >locking would actually be sequential at the machine-code level. Could you use >an OS call to exclude all other processes during locking and revoke that >privilege when completed? You could lock a single global lock, grab all the "real" locks you need, and then unlock the global one. The single global lock can be an OS call, but it's probably cheaper if it's not. But either way, you've got a single global bottleneck. Anyway, it's not as expensive as I thought on first glance, and it avoids some deadlocks. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Don Harrison 1996-05-01 0:00 ` David Hopwood 1996-05-01 0:00 ` Don Harrison @ 1996-05-02 0:00 ` Robert A Duff 1996-05-03 0:00 ` Don Harrison 1996-05-10 0:00 ` Don Harrison 2 siblings, 2 replies; 218+ messages in thread From: Robert A Duff @ 1996-05-02 0:00 UTC (permalink / raw) In article <Dqpo7r.HxD@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >Comments? Do you love it? Hate it? Don't care? At first glance, it seems like a lot of added complexity, for not enough benefit. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Robert A Duff @ 1996-05-03 0:00 ` Don Harrison 1996-05-10 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-03 0:00 UTC (permalink / raw) Robert A Duff writes: :In article <Dqpo7r.HxD@assip.csasyd.oz>, :Don Harrison <donh@syd.csa.com.au> wrote: :>Comments? Do you love it? Hate it? Don't care? : :At first glance, it seems like a lot of added complexity, for not enough :benefit. Where do you see the added complexity? I don't think dispatching would be a problem; you would just have extra levels. The drawback that I do see so far is most parameters would have to be exlicitly declared as frozen when you only want single dispatching (as in Ada) rather than being implicitly classwide (because the operation is not primitive to that parameter's class). However, the benefits are significant, IMO: a) Higher modelling integrity for two reasons: - Extraneous classes would seldom (never?) be required. - Encapsulation of abstractions (ovelapping on actions) would reflect reality more closely. b) Symmetry would enhance correctness (eg. locking in concurrency). :- Bob /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Robert A Duff 1996-05-03 0:00 ` Don Harrison @ 1996-05-10 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-10 0:00 UTC (permalink / raw) Bob Duff writes: :In article <Dqpo7r.HxD@assip.csasyd.oz>, :Don Harrison <donh@syd.csa.com.au> wrote: :>Comments? Do you love it? Hate it? Don't care? : :At first glance, it seems like a lot of added complexity, for not enough :benefit. Thinking some more about multiple dispatching, I'm rapidly coming to the conclusion that it's not such a good idea. Single dispatching seems to maximise the power of polymorphism because the same code can be executed for a multitude of different conformant objects. I guess this is sufficient for the vast majority of modelling tasks. While there are situations where a unique action must be taken for each specific combination of parameters, these would be uncommon and typically represent esoteric situations which may be dealt with using non-OO techniques. If you mean that multiple dispatching is unnecessary overkill, I agree. :- Bob /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Don Harrison donh@syd.csa.com.au ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Robert Dewar ` (3 preceding siblings ...) 1996-05-01 0:00 ` Don Harrison @ 1996-05-08 0:00 ` Joachim Durchholz 4 siblings, 0 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-05-08 0:00 UTC (permalink / raw) amitp@Xenon.Stanford.EDU wrote 07.05.96 on Re: Real OO: > What the OO books don't tell you is that the same thing is true for > the other `wrong' solution: objects when you want to add new > operations. If operations are methods, and each module contains one > data variant, then to add a new method you must modify *every* module > in your system. OO textbooks usually don't tell you too much about practical problems. (There's even a reason - an introduction on OO techniques that would list all the solutions would easily exceed the Encyclopedia Britannica.) However, I found the book "Design Patterns" by Gamma et al. invaluable. Basically, it is an excellent list of tricks of the trade, viewed from an OO standpoint. With the right Pattern, it is quite easy to extend the system in the Code direction even if you do it all in the OO way. The book explains the pros and cons of each pattern, too, which is more than can be said for most other books. E.g. organizing your OO system to be extensible in the Code direction usually makes it difficult (or much work) to extend it in the Data direction, and vice versa; but there are OO patterns for both types of organization. -Joachim -- Looking for a new job. Resume available on request. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-29 0:00 ` Jon S Anthony 1996-04-30 0:00 ` Robert Dewar @ 1996-05-03 0:00 ` Don Harrison 1996-05-03 0:00 ` Dave Fitch 1996-05-07 0:00 ` Jon S Anthony 1 sibling, 2 replies; 218+ messages in thread From: Don Harrison @ 1996-05-03 0:00 UTC (permalink / raw) Jon S Anthony writes: :In article <DqAyLG.D47@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: [...] :First, classwide operations are not primitive operations and so do not :constitute the "definition" of the type. So, even from a s called :"purest" view there's nothing wrong with putting them elsewhere. :Second, when they have more than one parameter (the typical case by :far) which type/calss would they be "assigned" to? Makes no sense and :is a very artificial constraint. Pretty much agree. : Third, you do this sort of thing in :Eiffel as well: anytime you are not "just" defining a type (for :example, the "main" routine), you just have to use a contrived class. See below. [...] :> : Even with some clairvoyance you'd have trouble as you :> :couldn't know about S when T and U were defined. So, one possible :> :out would be to subclass them all and put the new frozen features :> :in these new "extraneous" classes and use them instead. :> :> Hopefully, these comments are no longer relevant. : :No, these comments are still relevant. First, clients can't define :them. Second, you still need to be clairvoyant (define the operation :in one of the classes - even if it is application specific) or :introduce contrived/extraneous classes. Another way to look at this :is that classwide operations allow you to take arbitrary universal :views of things - without having to go back and inappropriately fudge :the class definitions. Sorry, I don't see the problem. All you need to do is add the new operation to one of the classes. The supplier still declares it and you don't need to know ahead of time that it is required. It's semantically exactly the same as shoving it in a package somewhere. No extraneous classes are necessary. [...] :> Is this what is meant by multiple dispatching? : :Yes.(tm/Kosh) What's tm/Kosh? :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-03 0:00 ` Don Harrison @ 1996-05-03 0:00 ` Dave Fitch 1996-05-07 0:00 ` Jon S Anthony 1 sibling, 0 replies; 218+ messages in thread From: Dave Fitch @ 1996-05-03 0:00 UTC (permalink / raw) donh@syd.csa.com.au (Don Harrison) writes: >Jon S Anthony writes: >>donh@syd.csa.com.au (Don Harrison) writes: >:> Is this what is meant by multiple dispatching? >: >:Yes.(tm/Kosh) > >What's tm/Kosh? "tm" meaning "trademark" I'd guess and Kosh being someone's name. It was a reference to "Babylon 5" (a science fiction tv show). Not very popular here (and on 11pm Tuesday nights), but quite good none the less (although a bit too yankie sometimes). There's an alien called Kosh who swans around making very cryptic replies to anyone's questions, usually consisting of odd beeping/singing noises followed by "yes" in a sing-song voice. Dave. -- _______________________________________________________________________ | . | | Rev. David Fitch _--_|\ "I am minuspeptic, frasmotic, even | | davidf@syd.csa.com.au / \ compunctuous to have caused you | | Ph : +61 2 9901 1413 \_.--._/ such perricabobulations." | | Fax : +61 2 9901 1616 v E. Blackadder to Dr Samuel Johnson | |_____________________________________________________________________| ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-03 0:00 ` Don Harrison 1996-05-03 0:00 ` Dave Fitch @ 1996-05-07 0:00 ` Jon S Anthony 1 sibling, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-07 0:00 UTC (permalink / raw) In article <Dqt13q.7AI@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > :> : Even with some clairvoyance you'd have trouble as you > :> :couldn't know about S when T and U were defined. So, one possible > :> :out would be to subclass them all and put the new frozen features > :> :in these new "extraneous" classes and use them instead. > :> > :> Hopefully, these comments are no longer relevant. > : > :No, these comments are still relevant. First, clients can't define > :them. Second, you still need to be clairvoyant (define the operation > :in one of the classes - even if it is application specific) or > :introduce contrived/extraneous classes. Another way to look at this > :is that classwide operations allow you to take arbitrary universal > :views of things - without having to go back and inappropriately fudge > :the class definitions. > > Sorry, I don't see the problem. All you need to do is add the new > operation to one of the classes. The supplier still declares it and > you don't need to know ahead of time that it is required. It's > semantically exactly the same as shoving it in a package > somewhere. No extraneous classes are necessary. The point is you either a) have to go back and hack the original class (your current suggestion in this paragraph) or b) add some contrived class. The former a) case is clearly _much_ more egregious and Joachim wisely goes for the latter b) case. Now, the b) case can be shrugged off as not being that big of a deal (as Joachim, again wisely does...) but it is really pretty ugly to create conceptual entities (as reflected by the contrived class...) just to pull this off. > :> Is this what is meant by multiple dispatching? > : > :Yes.(tm/Kosh) > > What's tm/Kosh? Already covered by someone else :-) /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] 218+ messages in thread
* Re: Real OO 1996-04-23 0:00 ` Don Harrison 1996-04-24 0:00 ` Don Harrison 1996-04-29 0:00 ` Jon S Anthony @ 1996-04-30 0:00 ` Joachim Durchholz 1996-04-30 0:00 ` Jon S Anthony 3 siblings, 0 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-04-30 0:00 UTC (permalink / raw) dewar@cs.nyu.edu wrote 30.04.96 on Re: Real OO: > [Stuff on overzealous language design goals deleted] I wholeheartedly agree. > The world of operations and types can be though of as a matrix: > > type 1 type 2 type 3 ... > > op1 x - x > op2 - x - > op3 x x - > ... > > Roughtly speaking, the OO approach organizes by columns, which is often > fine, but the operation organization by rows also often makes sense. I'm not too sure wether this actually makes much sense. Usually, if you have related functions, they work on similar types. Basically, this observation is the reason why OO is better suited than functional decomposition. And you're right, making the class the only way to structure a program (or library) takes things too far. There should be a place to put all the trigonometric etc. functions even after class REAL (or REAL_INTERVAL or FRACTION or whatever) has been defined, implemented, and closed for good. Still, you can create a mixin class. Such a class would be a function library in disguise, but it would work. I don't even think it is distasteful to abuse the class mechanism in this way. It is just the same as with subroutines and local variables - often I don't need more than a name for a chunk of code, but I don't have macros, I must use a subroutine, and I don't even think much about it. -Joachim -- Im speaking for myself here. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-23 0:00 ` Don Harrison ` (2 preceding siblings ...) 1996-04-30 0:00 ` Joachim Durchholz @ 1996-04-30 0:00 ` Jon S Anthony 1996-05-01 0:00 ` Matt Kennel ` (2 more replies) 3 siblings, 3 replies; 218+ messages in thread From: Jon S Anthony @ 1996-04-30 0:00 UTC (permalink / raw) In article <DqAyLG.D47@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > :>a) a type has been redefined to a non-conformant type, or > :>b) a routine has been made inaccessible due to a change in it's export status. > :> > :>At first sight, it seems silly to permit such things if they can > :>cause problems with polymorphism but there is a worthy motivation for > :>providing such flexibility: > :> > :>allowing reuse of legacy code that does not exactly fit your requirements. > :^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > > :I don't quite follow this. It does not seem to have much of anything > :to do with the problem of system validity checks. I suppose you can > :claim that overiding export policies and making covariant routine > :signatures (I think you mean conforming, not non-conforming) is > :is necessary to the emphasized goal, but I don't see it. > > Yes, I was wrong. It should be conforming signatures, as you say. (I > was trying to avoid looking it up). A good example illustrating both > a) and b) is in ETL, 22.6 - Notes on the type policy. > >[Eiffel example snipped] > > : This goal is > :trivial to achieve in Ada, do in large part to separation of > :specifications (interfaces) and implementations (yeah, that old > :argument). Add to this separation of interface and implementation > :inheritance, and these problems disappear. > > Can you explain how these avoid the problem? First, this is a really bad design (even for an example) as it violates any of the (several) reasonable definitions of is-a semantics. Second, this example has nothing to do with "legacy" code or reuse (which is what you claimed was important and I was refering to). But, let's take it at face value. Neither the a) no b) case is a problem - not even an issue - in Ada. This is mainly due to the separation of specific and classwide types. The example in Ada: package Drivers is type Driver is tagged private; ... end Drivers; package Drivers.Cars is type Car_Driver is new Driver with private; ... end Drivers.Cars; package Drivers.Trucks is type Truck_Driver is new Driver with private; ... end Drivers.Trucks; with Drivers; use Drivers; package Vehicles is type Vehicle is tagged private; procedure Register_Driver (V: Vehicle; D: Driver); procedure Renew_Rego_By_Mail (V: Vehicle); ... end Vehicles; with Drivers.Cars; use Drivers.Cars; package Vehicles.Cars is type Car is new Vehicle with private; procedure Register_Driver (V: Vehicle; D: Car_Driver); ... end Vehicles.Cars; with Drivers.Trucks; use Drivers.Trucks; package Vehicles.Trucks is type Truck is new Vehicle with private; procedure Register_Driver (T: Truck; D: Truck_Driver); procedure Renew_Rego_By_Inspection(T: Truck); ... end Vehicles.Trucks; -- In a client somewhere -- D: Driver; Cd: Car_Driver; V: Vehicle; T: Truck; ... -- Option 1. -- D := Cd; -- Illegal, compile time error V := T; -- Illegal, compile time error -- Option 2. -- D := Driver(Cd); -- OK, convert toward root V := Vehicle(T); -- OK, convert toward root Register_Driver(V, D); -- just fine and no dispatch needed -- Option 3. -- Register_Driver(Vehicle'Class(T),Cd); -- Compile error, no primitive ops -- Option 4. -- Register_Driver(Truck'Class(T), Cd); -- Compile error, no primitive ops -- Option 5. -- Register_Driver(Car'Class(T), Cd); -- Compile error, trucks not in -- Car'Class -- Option 6. -- Register_Driver(Vehicle'Class(V), D); -- Fine Register_Driver(Vehicle'Class(T), D); -- Fine (calls inherited op!!) As you can see, you can't get the invalid system stuff in the Ada model. In particular, you should note that the Truck type has _two_ primitive operations Register_Driver: procedure Register_Driver(V: Truck; D: Driver); -- Inherited from Vehicle procedure Register_Driver(V: Truck; D: Truck_Driver); -- *New* primitive op WRT b), note that in Ada, you cannot remove operations from derived types - they can always be used in any context where a parent can be used. To achieve this sort of effect, you would need to define Truck differently: package Vehicles.Trucks is type Truck is tagged private; -- Interface indicates new type procedure Register_Driver (T: Truck; D: Truck_Driver); procedure Renew_Rego_By_Inspection(T: Truck); ... end Vehicles.Trucks; Lastly, if you really wanted to do an a) like thing, a _client_ could define the exact semantics of it so that it "works" (at least to the extent that the client i) knowingly forced the issue, ii) had to supply semantics that did not violate the rules, and iii) knows what he's doing...) For example, -- in client... -- procedure Register_Driver (V: Vehicle'Class; D: Driver'Class) is -- -- Eats anything... ... begin ... if V in Truck'Class and not D in Truck_Driver'Class then -- -- Do something "appropriate"... Hope I know what I'm doing! ... elsif V in Car'Class and not D in Car_Driver'Class then -- -- Similar special sort of stuff... else -- Register_Driver(V, Driver(D)); end if; ... end Register_Driver; > :> for runtime. (Yes, I know that few, if any, Eiffel vendors implement > :> it). Don't know what you mean here. > : > :I mean simply that invalid Eiffel systems are knowingly accepted by > :these implementations. > > Correct. Note that this does _not_ happen in the Ada case and you loose no "flexibility" or what-have-you. In fact the Ada model seems to be significantly more flexible (this is not surprising as you have more semantic information to work with!) > :> : And the reason it is a problem > :> :is that Eiffel source does not have as much semantic information in it > :> :in this area. > :> > :> What might that be? > : > :Specifically, the distinction and use of the difference between class > :and type. The funny thing is, all this brouhaha about system validity > :concerns this distinction and an attempt to try to separate them > :again. > > No. This is not why. It's due to the distinction between static and dynamic > types. Hmmm, Meyer seems to disagree: ETL 22.4 (System Level Validity) "...but with generic derivation, expansion and anchored types we will need to reintroduce the distinction between types and classes." > : Meyer's "dynamic type set" is pretty much an Ada class and his > :"dynamic class set" is pretty much a class-wide type. > > No. There is not really an equivalent concept in Ada because it does not > support generic tagged types (as far as I'm aware) or anchored types. Sure it does, and as for anchored - they are _subsumed_ by classwide types > The dynamic type set is the set of all possible dynamic types whereas the > dynamic class set is the set of all classes these are derived from. > A generic class in the dynamic class set becomes numerous types in the > dynamic type set. I didn't say they were _identical_ concepts, I was trying to point out some similarity so that you might notice that basically, in Ada, the two concepts are made explicit in the notion of classwide types. For a given specific type T, T'Class is defined at the set of types rooted at T in a type derivation tree. If we have an object X : T'Class := some_value; X can take on any value of any type in T'Class (including T'Class). This also holds in the case of generic formals. That pretty much covers all that can be said about dynamic type/class sets. It's really very nice to be able to explicitly have control over how, where, and when these things are used. /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] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Jon S Anthony @ 1996-05-01 0:00 ` Matt Kennel 1996-05-03 0:00 ` Don Harrison 1996-05-02 0:00 ` Don Harrison 1996-05-06 0:00 ` Don Harrison 2 siblings, 1 reply; 218+ messages in thread From: Matt Kennel @ 1996-05-01 0:00 UTC (permalink / raw) Jon S Anthony (jsa@organon.com) wrote: : First, this is a really bad design (even for an example) as it : violates any of the (several) reasonable definitions of is-a : semantics. Second, this example has nothing to do with "legacy" code : or reuse (which is what you claimed was important and I was refering : to). But, let's take it at face value. Neither the a) no b) case is : a problem - not even an issue - in Ada. This is mainly due to the : separation of specific and classwide types. : The example in Ada: : package Drivers is : type Driver is tagged private; : ... : end Drivers; : package Drivers.Cars is : type Car_Driver is new Driver with private; : ... : end Drivers.Cars; : package Drivers.Trucks is : type Truck_Driver is new Driver with private; : ... : end Drivers.Trucks; : with Drivers; use Drivers; : package Vehicles is : type Vehicle is tagged private; : procedure Register_Driver (V: Vehicle; D: Driver); : procedure Renew_Rego_By_Mail (V: Vehicle); : ... : end Vehicles; : with Drivers.Cars; use Drivers.Cars; : package Vehicles.Cars is : type Car is new Vehicle with private; : procedure Register_Driver (V: Vehicle; D: Car_Driver); : ... : end Vehicles.Cars; : with Drivers.Trucks; use Drivers.Trucks; : package Vehicles.Trucks is : type Truck is new Vehicle with private; : procedure Register_Driver (T: Truck; D: Truck_Driver); : procedure Renew_Rego_By_Inspection(T: Truck); : ... : end Vehicles.Trucks; Sather: abstract class $DRIVERS is end; class CAR_DRIVER < $DRIVERS is end; class TRUCK_DRIVER < $DRIVERS is end; class VEHICLE is register_driver(d:$DRIVER); end; class CAR is include VEHICLE; register_driver(d:CAR_DRIVER); end; class TRUCK is include VEHICLE; register_driver(d:TRUCK_DRIVER); end; Though I'd personally not rely on the punning, and would rather do it with a distinct name. class VEHICLE is register_driver_if_valid(d:$DRIVER):BOOL; -- return success end; : jsa@organon.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Matt Kennel @ 1996-05-03 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-03 0:00 UTC (permalink / raw) Matt Kennel writes: [...] :Sather: : :abstract class $DRIVERS is Shouldn't this be a concrete? class to be equivalent to the Eiffel example? [...] :class VEHICLE is : register_driver_if_valid(d:$DRIVER):BOOL; -- return success :end; Aren't side-effect free functions part of Sather philosophy? -- /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Jon S Anthony 1996-05-01 0:00 ` Matt Kennel @ 1996-05-02 0:00 ` Don Harrison 1996-05-02 0:00 ` Robert I. Eachus ` (2 more replies) 1996-05-06 0:00 ` Don Harrison 2 siblings, 3 replies; 218+ messages in thread From: Don Harrison @ 1996-05-02 0:00 UTC (permalink / raw) Jon S Anthony writes: :In article <DqAyLG.D47@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: : :> :> Can you explain how these avoid the problem? : :First, this is a really bad design (even for an example) as it :violates any of the (several) reasonable definitions of is-a :semantics. So, you're saying is car and truck drivers are not drivers and cars and trucks are not vehicles. Right? I can see that. : Second, this example has nothing to do with "legacy" code :or reuse (which is what you claimed was important and I was refering :to). Yes, my original statement was based on a misunderstanding that routines could be redefined with non-conformant parameters. If this were true, then the statement would have been true. No, the issue of system validity arises because certain rules are relaxed to allow legitimate reuse of existing software. : But, let's take it at face value. Neither the a) no b) case is :a problem - not even an issue - in Ada. This is mainly due to the :separation of specific and classwide types. Your later example is closer semantically to my Eiffel example, but a few comments are in order: : :The example in Ada: : :package Drivers is : type Driver is tagged private; :.... :end Drivers; : :package Drivers.Cars is : type Car_Driver is new Driver with private; :.... :end Drivers.Cars; : :package Drivers.Trucks is : type Truck_Driver is new Driver with private; :.... :end Drivers.Trucks; : : : :with Drivers; use Drivers; :package Vehicles is : type Vehicle is tagged private; : : procedure Register_Driver (V: Vehicle; D: Driver); : procedure Renew_Rego_By_Mail (V: Vehicle); :.... :end Vehicles; : :with Drivers.Cars; use Drivers.Cars; :package Vehicles.Cars is : type Car is new Vehicle with private; : : procedure Register_Driver (V: Vehicle; D: Car_Driver); :.... :end Vehicles.Cars; : :with Drivers.Trucks; use Drivers.Trucks; :package Vehicles.Trucks is : type Truck is new Vehicle with private; : : procedure Register_Driver (T: Truck; D: Truck_Driver); : procedure Renew_Rego_By_Inspection(T: Truck); :.... :end Vehicles.Trucks; : : :-- In a client somewhere :-- : D: Driver; : Cd: Car_Driver; : V: Vehicle; : T: Truck; :.... : -- Option 1. : -- : D := Cd; -- Illegal, compile time error : V := T; -- Illegal, compile time error Okay. : -- Option 2. : -- : D := Driver(Cd); -- OK, convert toward root : V := Vehicle(T); -- OK, convert toward root : Register_Driver(V, D); -- just fine and no dispatch needed Not okay, if the objects get truncated in the type conversions. :-( Are they? Also, is it legal to write: V : Vehicle'Class; ... T := Truck(V); without supplying an aggregate containing the missing attributes? If both of these are legal, both may result in inconsistent objects being assigned. Neither is possible in Eiffel. : -- Option 3. : -- : Register_Driver(Vehicle'Class(T),Cd); -- Compile error, no primitive ops : : -- Option 4. : -- : Register_Driver(Truck'Class(T), Cd); -- Compile error, no primitive ops : : -- Option 5. : -- : Register_Driver(Car'Class(T), Cd); -- Compile error, trucks not in : -- Car'Class All okay. : -- Option 6. : -- : Register_Driver(Vehicle'Class(V), D); -- Fine : Register_Driver(Vehicle'Class(T), D); -- Fine (calls inherited op!!) Not okay, because this uses a truncated object. :-( :As you can see, you can't get the invalid system stuff in the Ada model. No, this example illustrates something else. :-( :In particular, you should note that the Truck type has _two_ primitive :operations Register_Driver: : : procedure Register_Driver(V: Truck; D: Driver); -- Inherited from Vehicle : procedure Register_Driver(V: Truck; D: Truck_Driver); -- *New* primitive op The more, the merrier. :WRT b), note that in Ada, you cannot remove operations from derived :types - they can always be used in any context where a parent can be :used. To achieve this sort of effect, you would need to define Truck :differently: : :package Vehicles.Trucks is : : type Truck is tagged private; -- Interface indicates new type : : procedure Register_Driver (T: Truck; D: Truck_Driver); : procedure Renew_Rego_By_Inspection(T: Truck); :.... :end Vehicles.Trucks; which means you are prevented from reusing the Vehicle abstraction even though it may have a lot of other stuff that is useful and no problem to you. :Lastly, if you really wanted to do an a) like thing, a _client_ could :define the exact semantics of it so that it "works" (at least to the :extent that the client i) knowingly forced the issue, ii) had to :supply semantics that did not violate the rules, and iii) knows what :he's doing...) For example, The purpose of system validity checking is precisely to ensure that the client *does* supply the correct semantics (ie. the correct parameters in the case of a)). The idea is to stop them *before runtime* from doing the wrong thing when they *don't* know what they are doing. The difference in Ada is that this responsibility is transferred to runtime, as you show below: :-- in client... :-- : :procedure Register_Driver (V: Vehicle'Class; D: Driver'Class) is :-- :-- Eats anything... :.... :begin :.... : if V in Truck'Class and not D in Truck_Driver'Class then : -- : -- Do something "appropriate"... Hope I know what I'm doing! The only appropriate thing to do here is raise an exception to signal to the client that what they are attempting is not on. It indicates a logic error in the program which, at the very least should be dealt with (and logged) by the client, or better still, corrected by the developer. ... : elsif V in Car'Class and not D in Car_Driver'Class then : -- : -- Similar special sort of stuff... Ditto. : else : -- : Register_Driver(V, Driver(D)); : end if; :.... :end Register_Driver; What you have described here is precisely a *runtime* system validity check. So, yes, you do have the problem of catcalls in Ada. The difference is that they are manifested at runtime. If Eiffel system validity checking does ever get implemented by vendors, by applying reasonable constraints, then it will be preferable, IMO to what Ada offers because it is better to resolve bugs as early as possible. In the meantime, Eiffel developers must include runtime checks like Ada developers if they want to keep out of mischief. But they still have the added flexibility of more permissive rules WRT reusing existing software. :> :> for runtime. (Yes, I know that few, if any, Eiffel vendors implement :> :> it). Don't know what you mean here. :> : :> :I mean simply that invalid Eiffel systems are knowingly accepted by :> :these implementations. :> :> Correct. : :Note that this does _not_ happen in the Ada case and you loose no :"flexibility" or what-have-you. In fact the Ada model seems to be :significantly more flexible (this is not surprising as you have more :semantic information to work with!) As far as I can see, the ability or otherwise to make a distinction between classwide and specific types is not relevant to this issue. [...] :> No. This is not why. It's due to the distinction between static and dynamic :> types. : :Hmmm, Meyer seems to disagree: : :ETL 22.4 (System Level Validity) : :"...but with generic derivation, expansion and anchored types we will : need to reintroduce the distinction between types and classes." I already corrected myself in an earlier post. You may have missed it. [stuff about the distinction between dynamic class set and dynamic type set] I think ETL is clear enough on the distinction between dynamic class set and dynamic type set and I don't see that it is relevant to whether or not catcalls are an issue in Ada. End of post :-) :-) :-) : : :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Don Harrison @ 1996-05-02 0:00 ` Robert I. Eachus 1996-05-02 0:00 ` Jon S Anthony 1996-05-06 0:00 ` Jon S Anthony 2 siblings, 0 replies; 218+ messages in thread From: Robert I. Eachus @ 1996-05-02 0:00 UTC (permalink / raw) In article <DqrqnC.2uz@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Also, is it legal to write: > V : Vehicle'Class; > ... > T := Truck(V); > without supplying an aggregate containing the missing attributes? > If both of these are legal, both may result in inconsistent > objects being assigned. > Neither is possible in Eiffel. Don, you keep assuming that, just because the models are different, there must be some gaping holes in the Ada 95 rules. There aren't, or at least there aren't any known violations of the obvious OO principles in any of the Ada tagged type rules. If there are any, I assure they are way out at the edges and will be fixed in the standard as found. Now to the specific case above. The declaration is illegal, but only because it must have an initial value: V: Vehicle'Class := Some_Truck; ...but this really doesn't affect your question. What happens in your example is called a view conversion rather than a value conversion. But when you track it down there is a rule 4.6(42) which applies to both which says: "The tag of the result is the tag of the operand. If the operand type is class-wide, a check is made that the tag of the operand identifies a (specific) type that is covered by or descended from the target type." In other words, if the Value of V is not a truck, or a of a type descended from Truck, you get an exception at run-time. In most cases the compiler will either optimize the check away or print a warning that an error will occur, but there are some cases where the check must be done at run-time. The practice in the Ada standard in such cases is to always require an exception, which allows different compilers to different degrees of checking at compile time without worrying whether this case requires a warning or is an error. Before you ask why Ada allows this, think about this "slightly" different version: V : Truck'Class := Some_Semi; ... T := Truck(V); This results in a value for T which doesn't have the additional information in objects of type Semi, but that is all right, it is now a Truck. Notice that no run-time checks are required. Whatever the value of V, it is convertable to a Truck. If you later wanted to do: Some_other_Semi := Semi(T); ...it wouldn't work. This is a case that is dectected at compile time. Instead you need to write: Some_other_Semi := Semi'(T with ...); ...and provide the missing components, if there are none, you write: Some_other_Semi := Semi'(T with null); So as everyone has been trying to tell Don all along, the Ada model is a generalization of the Eiffel model, and this results in more cases that require run-time checks. But if you don't like those checks, don't write that code! This especially applies to the multiple dispatch cases. (Jon's example omitted.) If you do write such code, Ada doesn't require that any set of actual parameters makes sense, all it does is to insure that something is done with them, even if it is an explicit nothing. Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... -- Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Don Harrison 1996-05-02 0:00 ` Robert I. Eachus @ 1996-05-02 0:00 ` Jon S Anthony 1996-05-03 0:00 ` Don Harrison 1996-05-06 0:00 ` Jon S Anthony 2 siblings, 1 reply; 218+ messages in thread From: Jon S Anthony @ 1996-05-02 0:00 UTC (permalink / raw) In article <DqrqnC.2uz@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > :First, this is a really bad design (even for an example) as it > :violates any of the (several) reasonable definitions of is-a > :semantics. > So, you're saying is car and truck drivers are not drivers and cars > and trucks are not vehicles. Right? I can see that. No. What I was refering to was that vehicles in general and cars and trucks in particular don't register drivers of any sort. > Your later example is closer semantically to my Eiffel example, but a few > comments are in order: Closer than what? >[snipped example] > : -- Option 2. > : -- > : D := Driver(Cd); -- OK, convert toward root > : V := Vehicle(T); -- OK, convert toward root > : Register_Driver(V, D); -- just fine and no dispatch needed > > Not okay, if the objects get truncated in the type conversions. :-( Are they? The tags of D and V (the dynamic types) do not get changed, only the corresponding values of the approppriate components get copied. The rules for this sort of thing are simply: 1. The tag (dynamic type) of an object never changes 2. Conversion is never away from the root So, there is no problem as nothing gets "truncated" and the operation is _statically_ bound to the exact operation. > Also, is it legal to write: > > V : Vehicle'Class; > ... > T := Truck(V); > > without supplying an aggregate containing the missing attributes? If both of > these are legal, both may result in inconsistent objects being assigned. Both are illegal: You can't have uniniitialized classwide objects (V needs a legal initialization). And the second is both an illegal conversion (Truck(V)) and assignment. What you are about is an extension aggregate. For example, T := (V with <here you must give the extra truck components>) > Neither is possible in Eiffel. And neither is possible in Ada. This sort of thing is too obvious to miss in any language design. > : -- Option 6. > : -- > : Register_Driver(Vehicle'Class(V), D); -- Fine > : Register_Driver(Vehicle'Class(T), D); -- Fine (calls inherited op!!) > > Not okay, because this uses a truncated object. :-( Huh?? Where?? No truncated objects around. Why do you think this???? HOW can you think this?? :-|. Note that a classwide "conversion" here (actually any conversion in this context) is a _view_ conversion. The entire object is completely unchanged. In the case above all that actually happens is that the calls are dispatched at runtime based on the dynamic type (tag) of the objects involved (V and T) and the chosen operation "gets" the complete object. Since you clearly do not understand what is happening here, why do you make a _pronouncement_ about it?!? One that is just plain _false_????? > :As you can see, you can't get the invalid system stuff in the Ada model. > > No, this example illustrates something else. :-( Yes, it illustrates that you don't know what you're talking about, but proceed to make pronouncements anyway... :-( :-( :-( > :In particular, you should note that the Truck type has _two_ primitive > :operations Register_Driver: > : > : procedure Register_Driver(V: Truck; D: Driver); -- Inherited from Vehicle > : procedure Register_Driver(V: Truck; D: Truck_Driver); -- *New* primitive op > > The more, the merrier. AGGHHHHHHHHHH!!!!!! Don, it is this fact that prevents the system validity problems. You just are not getting what is going on here. It is important to realize that because the types Driver and Truck_Driver are specific types that there are indeed TWO operations. You could override the one with the Driver signature, but by writing the other one with the _new_ specific type you create a new primitive operation. This then can be used to prevent the system validity problems that Eiffel has. > :WRT b), note that in Ada, you cannot remove operations from derived > :types - they can always be used in any context where a parent can be > :used. To achieve this sort of effect, you would need to define Truck > :differently: > : > :package Vehicles.Trucks is > : > : type Truck is tagged private; -- Interface indicates new type > : > : procedure Register_Driver (T: Truck; D: Truck_Driver); > : procedure Renew_Rego_By_Inspection(T: Truck); > :.... > :end Vehicles.Trucks; > > which means you are prevented from reusing the Vehicle abstraction > even though it may have a lot of other stuff that is useful and no > problem to you. Of course you are not prevented from this!!! Here is where you use the difference between interface and implementation "inheritance" because you have the split between _specification_ and _implementation_! ... Trucks as above... private type truck_impl is new vehicle with <truck stuff> type Truck is tagged record impl: truck_impl; end record; end Vehicles.Trucks; Now you simply have the operations dispatch on the implementation. Cake. And you don't violate any "substitution" principle. Really this is more of a win-win, where as the Eiffel is sort of a lose-lose. > :Lastly, if you really wanted to do an a) like thing, a _client_ could > :define the exact semantics of it so that it "works" (at least to the > :extent that the client i) knowingly forced the issue, ii) had to > :supply semantics that did not violate the rules, and iii) knows what > :he's doing...) For example, > > The purpose of system validity checking is precisely to ensure that > the client *does* supply the correct semantics (ie. the correct > parameters in the case of a)). The idea is to stop them *before > runtime* from doing the wrong thing when they *don't* know what they > are doing. The difference in Ada is that this responsibility is > transferred to runtime, as you show below: I am about to give up. What I show is how you _can_ forceably break things in a controlled way that are BROKEN FOR FREE in Eiffel. Why? Because this may well be a necessary evil under some circumstance and that in doing so you have to go _way_ out of your way to do it. Typically all the system validity problems of Eiffel are handled at _compile_ time in Ada as the above showed. You are just plain complete confused and in the weeds here. So far in the weeds it appears that no one can actually communicate with you... > :procedure Register_Driver (V: Vehicle'Class; D: Driver'Class) is > :-- > :-- Eats anything... > :.... > :begin > :.... > : if V in Truck'Class and not D in Truck_Driver'Class then > : -- > : -- Do something "appropriate"... Hope I know what I'm doing! > The only appropriate thing to do here is raise an exception to > signal to the client that what they are attempting is not on. It > indicates a logic error in the program which, at the very least > should be dealt with (and logged) by the client, or better still, > corrected by the developer. How do _you_ know what's appropriate here???? That's the point!! This is a case where the typical "correct" thing is WRONG!! The typical correct thing is handled at compile time in Ada. There may be all sorts of "reasonable" things to do! Log an error and then regroup by dispatching on the _perfectly valid and legal_ inherited Truck,Driver operation. > ... > : elsif V in Car'Class and not D in Car_Driver'Class then > : -- > : -- Similar special sort of stuff... > > Ditto. Ditto is right, .... > : else > : -- > : Register_Driver(V, Driver(D)); > : end if; > :.... > :end Register_Driver; > What you have described here is precisely a *runtime* system > validity check. So, yes, you do have the problem of catcalls in >lots of incorrect stuff blah blah blah... No. This example is _beyond_ system validity. There is no validity problem as such. You are just lost. You could attempt to do something like this in Eiffel (though it would be much uglier and convoluted) by using assignment attempt. > Ada. The difference is that they are manifested at runtime. If > Eiffel system validity checking does ever get implemented by > vendors, by applying reasonable constraints, then it will be > preferable, IMO to what Ada offers because it is better to resolve > bugs as early as possible. BUT Ada ALREADY DOES THIS!!!!! AIIIEEEEEEE!!!! COP A CLUE!!!!!! > include runtime checks like Ada developers if they want to keep out > of mischief. But they still have the added flexibility of more > permissive rules WRT reusing existing software. HOW???? You still haven't given even the slightest indication of how. THIS IS LUDICROUS! > :Note that this does _not_ happen in the Ada case and you loose no > :"flexibility" or what-have-you. In fact the Ada model seems to be > :significantly more flexible (this is not surprising as you have more > :semantic information to work with!) > > As far as I can see, the ability or otherwise to make a distinction between > classwide and specific types is not relevant to this issue. Yes, as far as you can see, but that's because you are IN THE WEEDS! > I think ETL is clear enough on the distinction between dynamic class > set and dynamic type set and I don't see that it is relevant to > whether or not catcalls are an issue in Ada. They are not an issue in Ada. They are not even relevant. But the dynamic class/type sets are pretty much _implicit_ things corresponding to classwide types. You really had me pulling my hair out on this one... /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] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Jon S Anthony @ 1996-05-03 0:00 ` Don Harrison 1996-05-06 0:00 ` Jon S Anthony 0 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-05-03 0:00 UTC (permalink / raw) Jon, Hope you have a nice weekend and don't get run over by a truck driven by someone licenced to drive only a car. :-) /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-03 0:00 ` Don Harrison @ 1996-05-06 0:00 ` Jon S Anthony 0 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-06 0:00 UTC (permalink / raw) In article <DqtAA4.8pG@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Jon, > > Hope you have a nice weekend and don't get run over by a truck driven by > someone licenced to drive only a car. :-) > > /// > Don. (o o) > =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- :-) :-) Actually, I _really_ stuck my neck out and am now co-owner of a Pitts S2A (aerobatic airplane). So, now you hope that I don't become a 'dart'! (hmmm, I don't think you really could turn a S2A into a dart and still be concious enough to regret it...) /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] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Don Harrison 1996-05-02 0:00 ` Robert I. Eachus 1996-05-02 0:00 ` Jon S Anthony @ 1996-05-06 0:00 ` Jon S Anthony 2 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-06 0:00 UTC (permalink / raw) In article <EACHUS.96May2150813@spectre.mitre.org> eachus@spectre.mitre.org (Robert I. Eachus) writes: [snip some lead in stuff...] > Now to the specific case above. The declaration is illegal, but > only because it must have an initial value: > > V: Vehicle'Class := Some_Truck; > > ...but this really doesn't affect your question. > > What happens in your example is called a view conversion rather > than a value conversion. But when you track it down there is a rule > 4.6(42) which applies to both which says: Yup, I mistakenly saw V as of the specific type Vehicle in the conversion/assignment bit when I wrote that it was illegal and needed an extension aggregate. This despite the fact that I apparently had enough sense to notice just above this that the classwide declaration needed an initialization. Go figure. Basically, Robert's account is very thorough here. /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] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Jon S Anthony 1996-05-01 0:00 ` Matt Kennel 1996-05-02 0:00 ` Don Harrison @ 1996-05-06 0:00 ` Don Harrison 1996-05-06 0:00 ` Don Harrison ` (2 more replies) 2 siblings, 3 replies; 218+ messages in thread From: Don Harrison @ 1996-05-06 0:00 UTC (permalink / raw) Jon, Stepping back to your original example, I'd like to clarify a few things before commenting on it (again). :The example in Ada: : :package Drivers is : type Driver is tagged private; :.... :end Drivers; : :package Drivers.Cars is : type Car_Driver is new Driver with private; :.... :end Drivers.Cars; : :package Drivers.Trucks is : type Truck_Driver is new Driver with private; :.... :end Drivers.Trucks; : : : :with Drivers; use Drivers; :package Vehicles is : type Vehicle is tagged private; : : procedure Register_Driver (V: Vehicle; D: Driver); I assume this syntax is correct. That is, you don't have to say Driver'Class? The status of D would be clearer, IMO, if you had to write Driver'Class. : procedure Renew_Rego_By_Mail (V: Vehicle); :.... :end Vehicles; : :with Drivers.Cars; use Drivers.Cars; :package Vehicles.Cars is : type Car is new Vehicle with private; : : procedure Register_Driver (V: Vehicle; D: Car_Driver); :.... :end Vehicles.Cars; : :with Drivers.Trucks; use Drivers.Trucks; :package Vehicles.Trucks is : type Truck is new Vehicle with private; : : procedure Register_Driver (T: Truck; D: Truck_Driver); : procedure Renew_Rego_By_Inspection(T: Truck); Presumably, Register_Driver does not override the implementation for Vehicle because of RM 6.3.1(15) and 8.3(8,9)? :.... :end Vehicles.Trucks; : : :-- In a client somewhere :-- : D: Driver; : Cd: Car_Driver; : V: Vehicle; : T: Truck; :.... : -- Option 1. : -- : D := Cd; -- Illegal, compile time error : V := T; -- Illegal, compile time error : : -- Option 2. : -- : D := Driver(Cd); -- OK, convert toward root : V := Vehicle(T); -- OK, convert toward root I understand these are view conversions, but am wondering how this can be implemented without reference semantics? : Register_Driver(V, D); -- just fine and no dispatch needed Is this this call statically bound to the implementation for Vehicle? : -- Option 3. : -- : Register_Driver(Vehicle'Class(T),Cd); -- Compile error, no primitive ops ... for type Truck with formal parameter Car_Driver? : -- Option 4. : -- : Register_Driver(Truck'Class(T), Cd); -- Compile error, no primitive ops ... for type Truck with formal parameter Car_Driver? : -- Option 6. : -- : Register_Driver(Vehicle'Class(V), D); -- Fine : Register_Driver(Vehicle'Class(T), D); -- Fine (calls inherited op!!) Shouldn't the first one be: Register_Driver(Truck'Class(T), D); -- Fine :As you can see, you can't get the invalid system stuff in the Ada model. :In particular, you should note that the Truck type has _two_ primitive :operations Register_Driver: : : procedure Register_Driver(V: Truck; D: Driver); -- Inherited from Vehicle : procedure Register_Driver(V: Truck; D: Truck_Driver); -- *New* primitive op And, shouldn't these be: : procedure Register_Driver(V: Vehicle; D: Driver); -- Inherited from Vehicle : procedure Register_Driver(T: Truck; D: Truck_Driver); -- *New* primitive op : :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-06 0:00 ` Don Harrison @ 1996-05-06 0:00 ` Don Harrison 1996-05-07 0:00 ` Jon S Anthony 1996-05-09 0:00 ` Jon S Anthony 2 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-06 0:00 UTC (permalink / raw) Not sure whether I put the right distribution on this, so here it is again. Jon, Stepping back to your original example, I'd like to clarify a few things before commenting on it (again). :The example in Ada: : :package Drivers is : type Driver is tagged private; :.... :end Drivers; : :package Drivers.Cars is : type Car_Driver is new Driver with private; :.... :end Drivers.Cars; : :package Drivers.Trucks is : type Truck_Driver is new Driver with private; :.... :end Drivers.Trucks; : : : :with Drivers; use Drivers; :package Vehicles is : type Vehicle is tagged private; : : procedure Register_Driver (V: Vehicle; D: Driver); I assume this syntax is correct. That is, you don't have to say Driver'Class? The status of D would be clearer, IMO, if you had to write Driver'Class. : procedure Renew_Rego_By_Mail (V: Vehicle); :.... :end Vehicles; : :with Drivers.Cars; use Drivers.Cars; :package Vehicles.Cars is : type Car is new Vehicle with private; : : procedure Register_Driver (V: Vehicle; D: Car_Driver); :.... :end Vehicles.Cars; : :with Drivers.Trucks; use Drivers.Trucks; :package Vehicles.Trucks is : type Truck is new Vehicle with private; : : procedure Register_Driver (T: Truck; D: Truck_Driver); : procedure Renew_Rego_By_Inspection(T: Truck); Presumably, Register_Driver does not override the implementation for Vehicle because of RM 6.3.1(15) and 8.3(8,9)? :.... :end Vehicles.Trucks; : : :-- In a client somewhere :-- : D: Driver; : Cd: Car_Driver; : V: Vehicle; : T: Truck; :.... : -- Option 1. : -- : D := Cd; -- Illegal, compile time error : V := T; -- Illegal, compile time error : : -- Option 2. : -- : D := Driver(Cd); -- OK, convert toward root : V := Vehicle(T); -- OK, convert toward root I understand these are view conversions, but am wondering how this can be implemented without reference semantics? : Register_Driver(V, D); -- just fine and no dispatch needed Is this this call statically bound to the implementation for Vehicle? : -- Option 3. : -- : Register_Driver(Vehicle'Class(T),Cd); -- Compile error, no primitive ops ... for type Truck with formal parameter Car_Driver? : -- Option 4. : -- : Register_Driver(Truck'Class(T), Cd); -- Compile error, no primitive ops ... for type Truck with formal parameter Car_Driver? : -- Option 6. : -- : Register_Driver(Vehicle'Class(V), D); -- Fine : Register_Driver(Vehicle'Class(T), D); -- Fine (calls inherited op!!) Shouldn't the first one be: Register_Driver(Truck'Class(T), D); -- Fine :As you can see, you can't get the invalid system stuff in the Ada model. :In particular, you should note that the Truck type has _two_ primitive :operations Register_Driver: : : procedure Register_Driver(V: Truck; D: Driver); -- Inherited from Vehicle : procedure Register_Driver(V: Truck; D: Truck_Driver); -- *New* primitive op And, shouldn't these be: : procedure Register_Driver(V: Vehicle; D: Driver); -- Inherited from Vehicle : procedure Register_Driver(T: Truck; D: Truck_Driver); -- *New* primitive op : :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-06 0:00 ` Don Harrison 1996-05-06 0:00 ` Don Harrison @ 1996-05-07 0:00 ` Jon S Anthony 1996-05-13 0:00 ` Don Harrison 1996-05-09 0:00 ` Jon S Anthony 2 siblings, 1 reply; 218+ messages in thread From: Jon S Anthony @ 1996-05-07 0:00 UTC (permalink / raw) In article <DqywnJ.4y8@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Jon, > > Stepping back to your original example, I'd like to clarify a few > things before commenting on it (again). > > :The example in Ada: > : > :package Drivers is > : type Driver is tagged private; > :.... > :end Drivers; > : > :package Drivers.Cars is > : type Car_Driver is new Driver with private; > :.... > :end Drivers.Cars; > : > :package Drivers.Trucks is > : type Truck_Driver is new Driver with private; > :.... > :end Drivers.Trucks; > : > : > : > :with Drivers; use Drivers; > :package Vehicles is > : type Vehicle is tagged private; > : > : procedure Register_Driver (V: Vehicle; D: Driver); > > I assume this syntax is correct. That is, you don't have to say > Driver'Class? The status of D would be clearer, IMO, if you had to > write Driver'Class. Yes, it is "correct", you don't need (and here you pretty clearly do not _want_) to say Driver'Class. As the example was proposed, there is no need to use Driver'Class. Instead of being "clearer", its use would be confusing and probably just plain wrong. > : procedure Renew_Rego_By_Mail (V: Vehicle); > :.... > :end Vehicles; > : > :with Drivers.Cars; use Drivers.Cars; > :package Vehicles.Cars is > : type Car is new Vehicle with private; > : > : procedure Register_Driver (V: Vehicle; D: Car_Driver); > :.... > :end Vehicles.Cars; > : > :with Drivers.Trucks; use Drivers.Trucks; > :package Vehicles.Trucks is > : type Truck is new Vehicle with private; > : > : procedure Register_Driver (T: Truck; D: Truck_Driver); > : procedure Renew_Rego_By_Inspection(T: Truck); > > Presumably, Register_Driver does not override the implementation for Vehicle > because of RM 6.3.1(15) and 8.3(8,9)? Visibility doesn't really enter into it (8.3 stuff) but 6.3.1(15) pretty much hits it. Truck_Driver and Driver are two _different_ types. The fact that they both belong in Driver'Class is not relevant to the declarations or implementation of operations of these two _specific_ types. It _is_ relevant to an _invocation_ of such operations where classwide operand(s) happen to be specified. > :.... > :end Vehicles.Trucks; > : > : > :-- In a client somewhere > :-- > : D: Driver; > : Cd: Car_Driver; > : V: Vehicle; > : T: Truck; > :.... > : -- Option 1. > : -- > : D := Cd; -- Illegal, compile time error > : V := T; -- Illegal, compile time error > : > : -- Option 2. > : -- > : D := Driver(Cd); -- OK, convert toward root > : V := Vehicle(T); -- OK, convert toward root > > I understand these are view conversions, but am wondering how this can be > implemented without reference semantics? Actually these are about the only example of such conversions which are not view conversions. All of the types involved are specific types and so the conversion "does the right thing" and merely changes the corresponding components of D and V to the values present in Cd and T. In no case are the tags (dynamic type) changed. In particular, no truncation occurs. View conversions (the most typical case...) when used in an invocation (the most typical case again...) _do_ use reference semantics. For example, the "better" way to have written the above Option 2 would have been to skip the D and V decls and the assignments and simply have written: Register_Driver(Vehicle(T), Driver(Cd)); -- Reference semantics... > : Register_Driver(V, D); -- just fine and no dispatch needed > > Is this this call statically bound to the implementation for Vehicle? Absolutely. That's the point of this option. > : -- Option 3. > : -- > : Register_Driver(Vehicle'Class(T),Cd); -- Compile error, no primitive ops > > ... for type Truck with formal parameter Car_Driver? Not quite - Vehicle has no primitive operations with _car_ drivers - only "generic" _drivers_. > : -- Option 4. > : -- > : Register_Driver(Truck'Class(T), Cd); -- Compile error, no primitive ops > > ... for type Truck with formal parameter Car_Driver? Yes, exactly. There are only operations for <Truck,Driver> and <Truck,Truck_Driver> pairs. > : -- Option 6. > : -- > : Register_Driver(Vehicle'Class(V), D); -- Fine > : Register_Driver(Vehicle'Class(T), D); -- Fine (calls inherited op!!) > > Shouldn't the first one be: > > Register_Driver(Truck'Class(T), D); -- Fine Depends on what you mean. As written the first one is just fine. All vehicles are in Vehicle'Class and D is delcared as a driver and there is a primitive Register_Driver op for the pair <vehicle,Driver> which is inherited (and possibly overridden) for every vehicle (note the non capitalized spelling of vehicle here). And as written your version is just fine for completely similar reasons (just substitute "truck" for "vehicle" and Truck for Vehicle.) From a legality point of view - neither have any legality issues, they are perfectly valid and "do the right thing." > :As you can see, you can't get the invalid system stuff in the Ada model. > :In particular, you should note that the Truck type has _two_ primitive > :operations Register_Driver: > : > : procedure Register_Driver(V: Truck; D: Driver); -- Inherited from Vehicle > : procedure Register_Driver(V: Truck; D: Truck_Driver); -- *New* primitive op > > And, shouldn't these be: > >:procedure Register_Driver(V: Vehicle; D: Driver); -- Inherited from Vehicle >:procedure Register_Driver(T: Truck; D: Truck_Driver); -- *New* primitive op The V in the second of mine should be a T as you have in your second as that is how it is declared in Vehicles.Trucks (though it _could_ have been given as a "V"). So, for this one, I made a typo... This is _really_ unfortunate as it probably just served to confuse you. Sorry. This really was a bad typo because the first one is quite correct as given and is the more important (and less obvious) bit. Vehicle and Truck are different _specific_ types and a specific type only covers itself. In a derivation an inherited operation is obtained from the parent version by "systematic replacement" of the corresponding types in the signature. Admittedly this is not particularly obvious to the casual observer. See 3.4(18-23) for the precise definition. From a "pragmatic" point of view, the important thing is to realize that there is indeed a new operation for the derived type with a signature specifying the new _specific_ type that is the derived type. /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] 218+ messages in thread
* Re: Real OO 1996-05-07 0:00 ` Jon S Anthony @ 1996-05-13 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-13 0:00 UTC (permalink / raw) In article <JSA.96May6211900@organon.com>, jsa@organon.com (Jon S Anthony) writes: [explanation of his Ada example answering questions I asked] Thanks for the explanation. It is clear now that the problem of broken polymorphism is not manifested in Ada due to an adherence to polymorphic (Liskov?) substitutability. My personal opinion is that a more liberal approach may be desirable sometimes but that is a different thread. : : :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : -- /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Don Harrison donh@syd.csa.com.au ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-06 0:00 ` Don Harrison 1996-05-06 0:00 ` Don Harrison 1996-05-07 0:00 ` Jon S Anthony @ 1996-05-09 0:00 ` Jon S Anthony 2 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-09 0:00 UTC (permalink / raw) Due to some shenanigans by certain nefarious net denizens, I have been informed that this post did not get out properly. Some places may have received it, and I apologies for any duplicates... /Jon In article <DqywnJ.4y8@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Jon, > > Stepping back to your original example, I'd like to clarify a few > things before commenting on it (again). > > :The example in Ada: > : > :package Drivers is > : type Driver is tagged private; > :.... > :end Drivers; > : > :package Drivers.Cars is > : type Car_Driver is new Driver with private; > :.... > :end Drivers.Cars; > : > :package Drivers.Trucks is > : type Truck_Driver is new Driver with private; > :.... > :end Drivers.Trucks; > : > : > : > :with Drivers; use Drivers; > :package Vehicles is > : type Vehicle is tagged private; > : > : procedure Register_Driver (V: Vehicle; D: Driver); > > I assume this syntax is correct. That is, you don't have to say > Driver'Class? The status of D would be clearer, IMO, if you had to > write Driver'Class. Yes, it is "correct", you don't need (and here you pretty clearly do not _want_) to say Driver'Class. As the example was proposed, there is no need to use Driver'Class. Instead of being "clearer", its use would be confusing and probably just plain wrong. > : procedure Renew_Rego_By_Mail (V: Vehicle); > :.... > :end Vehicles; > : > :with Drivers.Cars; use Drivers.Cars; > :package Vehicles.Cars is > : type Car is new Vehicle with private; > : > : procedure Register_Driver (V: Vehicle; D: Car_Driver); > :.... > :end Vehicles.Cars; > : > :with Drivers.Trucks; use Drivers.Trucks; > :package Vehicles.Trucks is > : type Truck is new Vehicle with private; > : > : procedure Register_Driver (T: Truck; D: Truck_Driver); > : procedure Renew_Rego_By_Inspection(T: Truck); > > Presumably, Register_Driver does not override the implementation for Vehicle > because of RM 6.3.1(15) and 8.3(8,9)? Visibility doesn't really enter into it (8.3 stuff) but 6.3.1(15) pretty much hits it. Truck_Driver and Driver are two _different_ types. The fact that they both belong in Driver'Class is not relevant to the declarations or implementation of operations of these two _specific_ types. It _is_ relevant to an _invocation_ of such operations where classwide operand(s) happen to be specified. > :.... > :end Vehicles.Trucks; > : > : > :-- In a client somewhere > :-- > : D: Driver; > : Cd: Car_Driver; > : V: Vehicle; > : T: Truck; > :.... > : -- Option 1. > : -- > : D := Cd; -- Illegal, compile time error > : V := T; -- Illegal, compile time error > : > : -- Option 2. > : -- > : D := Driver(Cd); -- OK, convert toward root > : V := Vehicle(T); -- OK, convert toward root > > I understand these are view conversions, but am wondering how this can be > implemented without reference semantics? Actually these are about the only example of such conversions which are not view conversions. All of the types involved are specific types and so the conversion "does the right thing" and merely changes the corresponding components of D and V to the values present in Cd and T. In no case are the tags (dynamic type) changed. In particular, no truncation occurs. View conversions (the most typical case...) when used in an invocation (the most typical case again...) _do_ use reference semantics. For example, the "better" way to have written the above Option 2 would have been to skip the D and V decls and the assignments and simply have written: Register_Driver(Vehicle(T), Driver(Cd)); -- Reference semantics... > : Register_Driver(V, D); -- just fine and no dispatch needed > > Is this this call statically bound to the implementation for Vehicle? Absolutely. That's the point of this option. > : -- Option 3. > : -- > : Register_Driver(Vehicle'Class(T),Cd); -- Compile error, no primitive ops > > ... for type Truck with formal parameter Car_Driver? Not quite - Vehicle has no primitive operations with _car_ drivers - only "generic" _drivers_. > : -- Option 4. > : -- > : Register_Driver(Truck'Class(T), Cd); -- Compile error, no primitive ops > > ... for type Truck with formal parameter Car_Driver? Yes, exactly. There are only operations for <Truck,Driver> and <Truck,Truck_Driver> pairs. > : -- Option 6. > : -- > : Register_Driver(Vehicle'Class(V), D); -- Fine > : Register_Driver(Vehicle'Class(T), D); -- Fine (calls inherited op!!) > > Shouldn't the first one be: > > Register_Driver(Truck'Class(T), D); -- Fine Depends on what you mean. As written the first one is just fine. All vehicles are in Vehicle'Class and D is delcared as a driver and there is a primitive Register_Driver op for the pair <vehicle,Driver> which is inherited (and possibly overridden) for every vehicle (note the non capitalized spelling of vehicle here). And as written your version is just fine for completely similar reasons (just substitute "truck" for "vehicle" and Truck for Vehicle.) From a legality point of view - neither have any legality issues, they are perfectly valid and "do the right thing." > :As you can see, you can't get the invalid system stuff in the Ada model. > :In particular, you should note that the Truck type has _two_ primitive > :operations Register_Driver: > : > : procedure Register_Driver(V: Truck; D: Driver); -- Inherited from Vehicle > : procedure Register_Driver(V: Truck; D: Truck_Driver); -- *New* primitive op > > And, shouldn't these be: > >:procedure Register_Driver(V: Vehicle; D: Driver); -- Inherited from Vehicle >:procedure Register_Driver(T: Truck; D: Truck_Driver); -- *New* primitive op The V in the second of mine should be a T as you have in your second as that is how it is declared in Vehicles.Trucks (though it _could_ have been given as a "V"). So, for this one, I made a typo... This is _really_ unfortunate as it probably just served to confuse you. Sorry. This really was a bad typo because the first one is quite correct as given and is the more important (and less obvious) bit. Vehicle and Truck are different _specific_ types and a specific type only covers itself. In a derivation an inherited operation is obtained from the parent version by "systematic replacement" of the corresponding types in the signature. Admittedly this is not particularly obvious to the casual observer. See 3.4(18-23) for the precise definition. From a "pragmatic" point of view, the important thing is to realize that there is indeed a new operation for the derived type with a signature specifying the new _specific_ type that is the derived type. /Jon -- Jon Anthony Organon Motives, Inc. 1 Williston Road, Suite 4 Belmont, MA 02178 617.484.3383 jsa@organon.com -- Jon Anthony Organon Motives, Inc. 1 Williston Road, Suite 4 Belmont, MA 02178 617.484.3383 jsa@organon.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Multiple Dispatch in Ada 95 (Was Re: Real OO) 1996-04-19 0:00 ` Don Harrison 1996-04-19 0:00 ` Jon S Anthony @ 1996-04-19 0:00 ` Brian Rogoff 1996-04-21 0:00 ` Brian Rogoff 1996-04-19 0:00 ` Robert I. Eachus ` (6 subsequent siblings) 8 siblings, 1 reply; 218+ messages in thread From: Brian Rogoff @ 1996-04-19 0:00 UTC (permalink / raw) Don Harrison writes: Jon S Anthony writes: ::> In Ada, with the second, third, fourth, ... and nth :> operands of the same specific type controlling dispatching, by the :> time the compiler gets to the end of the routine signature, it is :> rolling it's eyes and crying: "Good grief, I heard you the first :> time!!!" : :Don, are you really this clueless or are you just being facetious? :The reason I say this is that the above doesn't even work as a joke, :because it is rooted in an error (instead of a clever observation). Mostly facetious. But I do find it slightly ridiculous to say that there are multiple controlling operands if it only works if they happen to have the same dynamic type. (No, I'm not saying that it should be possible for them to be different). The fact that you get an exception if the types differ is not as plain as in the Eiffel paradigm, IMO. This raises an interesting question. Why doesn't Ada 95 support multimethods? I'm sure this was discussed somewhere, I just haven't seen it. Does multiple dispatch break something, or was it mainly an implementation thing? Could we see it in a future version of Ada? While Don clearly prefers the "Eiffel way", I think that multiple dispatch would fit in more neatly with the current Ada 95 scheme, which I like. -- Brian ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Multiple Dispatch in Ada 95 (Was Re: Real OO) 1996-04-19 0:00 ` Multiple Dispatch in Ada 95 (Was Re: Real OO) Brian Rogoff @ 1996-04-21 0:00 ` Brian Rogoff 0 siblings, 0 replies; 218+ messages in thread From: Brian Rogoff @ 1996-04-21 0:00 UTC (permalink / raw) Robert A Duff writes: Brian Rogoff <rogoff@sccm.stanford.edu> wrote: >Sure, I've seen the approach. What I was wondering is "What >criteria did the designers of Ada 95 use to decide against >supporting multimethods, I thought there was something in the Rationale about it, but I can't seem to find it by quickly flipping through (and the index isn't much help). I think the main reasons were just added complexity (complexity of the language, not so much complexity of the implementation, although that's part of it). With multi-methods, it becomes hard (for the user) to associate a particular method with a particular abstraction -- suppose I want a multi-method that dispatches on two types declared in two different packages -- where do I put it? Of course, coupled entities defined in different packages cause no end of problems ;-). I have no good answer; looking to other languages with multi-methods (CLOS, Dylan, Cecil, any others?) might suggest solutions, and things to avoid. The same is true of multilple inheritance. I remember discussing it at length with Tucker, and being overwhelmed by the number of extra rules and functionality needed to deal with name clashes and so forth. The Java approach of supporting multiple interface inheritance and only single inheritance of implementation seems to map well to Ada 95. What I find interesting about it is that it is implemented by forwarding in AdaJava, and yet I thought of it as being something you'd do with generic formal package parameters in Ada 95. I can see why it is done that way in AdaJava, I just find all of these idioms for dealing with signatures amusing. >...and is >it possible that the issue will be reevaluated in the future?". Anything's possible in Ada 0X. ;-) Also, nothing's stopping somebody from designing a language extension, adding it to GNAT or something, and playing with it to see if it makes sense. Well, I think that multi-methods would be less useful than Sather style iterators, so if I make an extension, that would be the one. -- Brian ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Multiple Dispatch in Ada 95 (Was Re: Real OO) 1996-04-19 0:00 ` Don Harrison 1996-04-19 0:00 ` Jon S Anthony 1996-04-19 0:00 ` Multiple Dispatch in Ada 95 (Was Re: Real OO) Brian Rogoff @ 1996-04-19 0:00 ` Robert I. Eachus 1996-04-20 0:00 ` Brian Rogoff ` (5 subsequent siblings) 8 siblings, 0 replies; 218+ messages in thread From: Robert I. Eachus @ 1996-04-19 0:00 UTC (permalink / raw) In article <ROGOFF.96Apr19092233@sccm.Stanford.EDU> rogoff@sccm.Stanford.EDU (Brian Rogoff) writes: > This raises an interesting question. Why doesn't Ada 95 support > multimethods? I'm sure this was discussed somewhere, I just haven't > seen it. Does multiple dispatch break something, or was it mainly an > implementation thing? Could we see it in a future version of Ada? It is very possible to do multiple dispatch in Ada 95, and I think I even posted the idiom recently. However, you had better know what you are doing! This is not a comment on Ada 95, but on writing operations with multiple (dispatching) parameters. In Ada you define a primitive for one class with a classwide parameter of the same or another class. The advantage--and disadvantage of this is that the dispatches are not simultanous but ordered. You have to be able to define the operation in way that is partitionable, but you gain the advantage that the operation is always well defined for any set of parameters. -- Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Multiple Dispatch in Ada 95 (Was Re: Real OO) 1996-04-19 0:00 ` Don Harrison ` (2 preceding siblings ...) 1996-04-19 0:00 ` Robert I. Eachus @ 1996-04-20 0:00 ` Brian Rogoff 1996-04-21 0:00 ` Robert A Duff 1996-04-24 0:00 ` Real OO Joachim Durchholz ` (4 subsequent siblings) 8 siblings, 1 reply; 218+ messages in thread From: Brian Rogoff @ 1996-04-20 0:00 UTC (permalink / raw) Robert I. Eachus writes: Brian Rogoff writes: > This raises an interesting question. Why doesn't Ada 95 support > multimethods? I'm sure this was discussed somewhere, I just haven't > seen it. Does multiple dispatch break something, or was it mainly an > implementation thing? Could we see it in a future version of Ada? It is very possible to do multiple dispatch in Ada 95, and I think I even posted the idiom recently. However, you had better know what you are doing! This is not a comment on Ada 95, but on writing operations with multiple (dispatching) parameters. In Ada you define a primitive for one class with a classwide parameter of the same or another class. Sure, I've seen the approach. What I was wondering is "What criteria did the designers of Ada 95 use to decide against supporting multimethods, and is it possible that the issue will be reevaluated in the future?". I know that there are lots of implementation complexities raised by having built in multimethods, but it appears to fit in well with the Ada 95 OO model. It may be that in their study of CLOS, the designers concluded that multi-methods were just not that useful. They seem to make the handling of binary methods a bit cleaner, IMO. There is a paper by Tucker Taft on why Ada doesn't have built-in multiple inheritance. I haven't seen any similar discussion as to why Ada 95 doesn't have multiple dispatch. The obvious issue of implementation difficulty is a good enough reason, but if that were the only one, I could imagine that compiler research could render it invalid in the future. As to your point about needing to know what you are doing, I agree. We could say the same thing about operator overloading, but I sure am glad that Ada has it! -- Brian ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Multiple Dispatch in Ada 95 (Was Re: Real OO) 1996-04-20 0:00 ` Brian Rogoff @ 1996-04-21 0:00 ` Robert A Duff 0 siblings, 0 replies; 218+ messages in thread From: Robert A Duff @ 1996-04-21 0:00 UTC (permalink / raw) In article <ROGOFF.96Apr20161406@sccm.Stanford.EDU>, Brian Rogoff <rogoff@sccm.stanford.edu> wrote: >Sure, I've seen the approach. What I was wondering is "What criteria did the >designers of Ada 95 use to decide against supporting multimethods, I thought there was something in the Rationale about it, but I can't seem to find it by quickly flipping through (and the index isn't much help). I think the main reasons were just added complexity (complexity of the language, not so much complexity of the implementation, although that's part of it). With multi-methods, it becomes hard (for the user) to associate a particular method with a particular abstraction -- suppose I want a multi-method that dispatches on two types declared in two different packages -- where do I put it? The same is true of multilple inheritance. I remember discussing it at length with Tucker, and being overwhelmed by the number of extra rules and functionality needed to deal with name clashes and so forth. >...and is >it possible that the issue will be reevaluated in the future?". Anything's possible in Ada 0X. ;-) Also, nothing's stopping somebody from designing a language extension, adding it to GNAT or something, and playing with it to see if it makes sense. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Don Harrison ` (3 preceding siblings ...) 1996-04-20 0:00 ` Brian Rogoff @ 1996-04-24 0:00 ` Joachim Durchholz 1996-05-01 0:00 ` Matt Kennel ` (3 more replies) 1996-04-30 0:00 ` Jon S Anthony ` (3 subsequent siblings) 8 siblings, 4 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-04-24 0:00 UTC (permalink / raw) jsa@organon.com wrote 19.04.96 on Re: Real OO: > Simply this: Classwide operations can be defined anywhere by anyone. > They do not have to be defined along with the primitive types in > their packages. Umm, that's not a real problem. Whenever I need an additional routine that the original class designer forgot, I create a mixin class that I inherit wherever I want to use it. I don't even have to insert it anywhere in the inheritance structure, as I can multiply-inherit it where I want it. Or I can write a class function_library[T -> INTEGER] and have an INTEGER library. Though I admit it requires me to write two or three additional lines of code (class header and final END), and I feel it is using a big concept (classes) for a small task (writing a simple routine). But I like to think that forcing me to write a class I also begin thinking about making a real class instead of just a function - and I have the impression that many functions are just that, poor abstraction that got through because the programmer wanted a quick-and-dirty solution. (Of course, if it is just a function even after analysis, I'm still stuck with a class instead of a function.) -Joachim -- Im speaking for myself here. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-24 0:00 ` Real OO Joachim Durchholz @ 1996-05-01 0:00 ` Matt Kennel 1996-05-02 0:00 ` Don Harrison 1996-05-07 0:00 ` Joachim Durchholz ` (2 subsequent siblings) 3 siblings, 1 reply; 218+ messages in thread From: Matt Kennel @ 1996-05-01 0:00 UTC (permalink / raw) : jsa@organon.com wrote 19.04.96 on Re: Real OO: : > Simply this: Classwide operations can be defined anywhere by anyone. : > They do not have to be defined along with the primitive types in : > their packages. And how is this different from Eiffel? class A is end class B is end class C is end class ANYWHERE_BY_ANYONE is this_is_what_ada_calls_a_classwide_operation(a:A,b:B,c:C) is ... end; end ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-01 0:00 ` Matt Kennel @ 1996-05-02 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-02 0:00 UTC (permalink / raw) Matt Kennel writes: :: jsa@organon.com wrote 19.04.96 on Re: Real OO: : :: > Simply this: Classwide operations can be defined anywhere by anyone. :: > They do not have to be defined along with the primitive types in :: > their packages. : :And how is this different from Eiffel? : :class A is : :end : :class B is : :end : :class C is : :end : : :class ANYWHERE_BY_ANYONE is : : this_is_what_ada_calls_a_classwide_operation(a:A,b:B,c:C) is ... end; : :end That's true, and there's nothing wrong with it. I take back my comment about spaghetti, however, note that a benefit of the 'Ziggy' approach is that the spaghetti may be unraveled so that the impact of redefining attributes of a class may be more easily seen because the operations which use it are shown in the merged view of the class. BTW, a further clarification. I wrote: : Comparison with Eiffel: : : 1) Each parameter to an operation has the same status: there is no Current object : and attributes of each parameter object may be updated directly : : eg. a.y := ... : b.x := ... : : This is more permissive than Eiffel. Before I'm accused of breaking encapsulation, what I mean is that reattachment validity would be as follows: do_y (a: A; b: B) is require a.p > 0 and b.s > 0 do a.p := ... -- legal a.p.k := ... -- illegal!!! some_op (a.p.k) -- legal a := b -- legal a := b.t -- legal end In other words, it's the same as Eiffel, except that the privileges of Current to update it's own attributes directly is extended to other parameters (because there are effectively multiple Currents) and you can use (the equivalent of) Current in assignments (without using wrapper classes?). I think that a general benefit of the design would be that there would be fewer situations where extraneous wrapper classes would be required thus enabling greater modelling integrity in that classes would tend to represent genuine abstractions rather than contrived ones conceived solely to work around the pure OO model. WRT, multiple dispatching, it should be noted that although it is the default, runtime efficiency is not compromised for equivalent Eiffel calls because they would be optimised statically into simpler calls depending on whether each parameter type is extended: a) simpler multiple dispatching calls (where one or more is extended) b) single dispatching (where only one parameter's type is extended) c) statically bound calls (where no parameter's type is extended). Of course, this is more work for the compiler/linker. Note that in this model, the class is retained as the sole means of encapsulation (unlike Ada, Dylan?). However, operations may be grouped (together with classes) outside the language in clusters just as in Eiffel. Some are no doubt thinking that I want to invent a new language. Not so. What I would like is for such a model be considered for Eiffel if it is considered a significant improvement. I think it would be, but that's just my opinion and there may be problems that I have overlooked. It is certainly not my intention to create a splinter language from Eiffel. Eiffel is a terrific language but still has flaws. What I would like to do is invite discussion on the idea in comp.lang.eiffel and if it is deemed a significant improvement, then a formal proposal could be made to NICE. If translation from Eiffel 3 to 'Ziggy' is straightforward, as it should be, then the idea of it becoming a future version of Eiffel has some credibility. /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-24 0:00 ` Real OO Joachim Durchholz 1996-05-01 0:00 ` Matt Kennel @ 1996-05-07 0:00 ` Joachim Durchholz 1996-05-08 0:00 ` Jon S Anthony 1996-05-09 0:00 ` Robert I. Eachus 3 siblings, 0 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-05-07 0:00 UTC (permalink / raw) jsa@organon.com wrote 06.05.96 on Re: Real OO: > > In Ada, I'd have to create lots of extraneous packages - doesn't > > sound much of a difference to me. You always need some syntactic > > sugar > > No, in Ada I can just write a function. Or procedure. I can also > write a package if I think it makes sense. So, no, there is no > similar requirement. Since classwide operations are not primitive, > they do not need to part of a package. Caught me - it's actually a few years since I looked at anything that related to Ada. Humm... let's see... the book says it's from 1981, and I bought it shortly after it was available. It's *really* been a long time. And now that I look at it, that ancient standard actually allows single routines to be compiled - I stand corrected. > > Still, classes are as good as any other concept for making a > > module, so why not use them? > > So, why not use classes for modules? Basically because that is not > really their major semantic intent (not just in "programming" but in > any taxonomic modeling scenario). Sure, you can say, "so what?", and > pull a Humpty Dumpty and further say, "in this nook of the world, they > do have this intent." Fine. But saying it's so doesn't make it so. > And I haven't seen a convincing argument for this position (maybe > there is one, and I just haven't seen it...) Well, it's the same with functions and routines. They were introduced to reuse code (rings a bell, doesn't it?). But nowadays I find myself using them just for naming a chunk of code. This is a blatant abuse of the concept of a subroutine; after all, I don't call the routine more than once! But in this nook of the world, a routine is the only way to name a piece of code. (Usually. I once programmed in a language that had such a feature. It was called ELAN, designed specifically for education, and I've never seen a commercial compiler for it.) > > Usually, an abstraction isn't embodied in a single class, but in > > a set of related classes. While the notion of "class" is > > heavyweigth, actual classes are often quite lightweight. > > Sounds like a subsystem. And it would be nice to have some construct > with which you could express this collection of classes within the > model that contains the particular notion of class with which you're > working. A construct whose job it was to collect and organize sets of > arbitrary sorts of things... Well, in Eiffel, these are called clusters. They aren't really part of the language standard, but ISE's compiler organizes the classes into clusters, and the other vendors follow suit here. It's somewhat alike the convention of naming include files *.h in C - never formalized, but everybody adheres. -Joachim -- Looking for a new job. Resume available on request. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-24 0:00 ` Real OO Joachim Durchholz 1996-05-01 0:00 ` Matt Kennel 1996-05-07 0:00 ` Joachim Durchholz @ 1996-05-08 0:00 ` Jon S Anthony 1996-05-09 0:00 ` Robert I. Eachus 3 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-08 0:00 UTC (permalink / raw) In article <68P6zfWk3RB@herold.franken.de> jhd@herold.franken.de (Joachim Durchholz) writes: > Caught me - it's actually a few years since I looked at anything that > related to Ada. Humm... let's see... the book says it's from 1981, and > I bought it shortly after it was available. It's *really* been a long > time. And now that I look at it, that ancient standard actually allows > single routines to be compiled - I stand corrected. That would be a "pre" standard. The Ada83 standard was in 1983. It's amazing how ancient all this stuff is. There are no new ideas... (the original Eiffel stuff is what? circa '83/'85?) > > So, why not use classes for modules? Basically because that is not > > really their major semantic intent (not just in "programming" but in > > any taxonomic modeling scenario). Sure, you can say, "so what?", and > > pull a Humpty Dumpty and further say, "in this nook of the world, they > > do have this intent." Fine. But saying it's so doesn't make it so. > > And I haven't seen a convincing argument for this position (maybe > > there is one, and I just haven't seen it...) > > Well, it's the same with functions and routines. They were introduced > to reuse code (rings a bell, doesn't it?). But nowadays I find myself > using them just for naming a chunk of code. > This is a blatant abuse of the concept of a subroutine; after all, I Why do you say that? I see no "abuse" of the concept at all. Subprograms stand for a piece of functionality. Whether you use it zero times, once, or a zillion times has no bearing on this. How this is implmented also has no bearing on the _concept_. I am speaking about what it is typically taken to stand for in a conceptual sense. So, no, there is no similarity here... > > Sounds like a subsystem. And it would be nice to have some construct > > with which you could express this collection of classes within the > > model that contains the particular notion of class with which you're > > working. A construct whose job it was to collect and organize sets of > > arbitrary sorts of things... > > Well, in Eiffel, these are called clusters. They aren't really part of > the language standard, but ISE's compiler organizes the classes into > clusters, and the other vendors follow suit here. It's somewhat alike > the convention of naming include files *.h in C - never formalized, > but everybody adheres. Yes, I know. They are (typically) part of LACE (hmmm, if they are actually called clusters, maybe the extra language environment that the implementation is offering has to be called LACE...) And since it is an add on, it is not as strong a notion and is YALL (yet another little language)... /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] 218+ messages in thread
* Re: Real OO 1996-04-24 0:00 ` Real OO Joachim Durchholz ` (2 preceding siblings ...) 1996-05-08 0:00 ` Jon S Anthony @ 1996-05-09 0:00 ` Robert I. Eachus 3 siblings, 0 replies; 218+ messages in thread From: Robert I. Eachus @ 1996-05-09 0:00 UTC (permalink / raw) In article <JSA.96May7205253@organon.com> jsa@organon.com (Jon S Anthony) writes: > That would be a "pre" standard. The Ada83 standard was in 1983. It's > amazing how ancient all this stuff is. There are no new ideas... Actually there have been three different Ada standards. Ada80 was MIL-STD-1815. Ada83 was ANSI/MIL-STD-1815A, ISO 8652:1987 and a lot of other national standards. Ada95 is ANSI/ISO/IEC-8652:1995. Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... -- Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Don Harrison ` (4 preceding siblings ...) 1996-04-24 0:00 ` Real OO Joachim Durchholz @ 1996-04-30 0:00 ` Jon S Anthony 1996-05-03 0:00 ` Don Harrison 1996-04-30 0:00 ` Joachim Durchholz ` (2 subsequent siblings) 8 siblings, 1 reply; 218+ messages in thread From: Jon S Anthony @ 1996-04-30 0:00 UTC (permalink / raw) In article <67SpgKdV3RB@herold.franken.de> jhd@herold.franken.de (Joachim Durchholz) writes: > > Simply this: Classwide operations can be defined anywhere by anyone. > > They do not have to be defined along with the primitive types in > > their packages. > > Umm, that's not a real problem. Whenever I need an additional > routine that the original class designer forgot, I create a mixin > class that I inherit wherever I want to use it. I don't even have > to insert it anywhere in the inheritance structure, as I can > multiply-inherit it where I want it. Or I can write a class > function_library[T -> INTEGER] and have an INTEGER library. Well, whether it is a "real" problem or not, you have to introduce contrived/extraneous classes in an attempt to define the things. > Though I admit it requires me to write two or three additional > lines of code (class header and final END), and I feel it is > using a big concept (classes) for a small task (writing a simple > routine). But I like to think that forcing me to write a class I > also begin thinking about making a real class instead of just a > function - and I have the impression that many functions are just > that, poor abstraction that got through because the programmer So what makes a function a "poor" abstraction??? In the right context (and the one under consideration is a good example), it can be the perfect abstraction. The real problem is, _all you have are classes_, and it just is plain not true that all the world's a class. > wanted a quick-and-dirty solution. (Of course, if it is just a > function even after analysis, I'm still stuck with a class > instead of a function.) Indeed. And in the scenario I presented this is exactly the situation. /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] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Jon S Anthony @ 1996-05-03 0:00 ` Don Harrison 1996-05-07 0:00 ` Jon S Anthony 0 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-05-03 0:00 UTC (permalink / raw) Jon S Anthony writes: [...] :So what makes a function a "poor" abstraction??? In the right :context (and the one under consideration is a good example), it :can be the perfect abstraction. The real problem is, _all you :have are classes_, and it just is plain not true that all the :world's a class. The types of Eiffel (and probably other language's) classes that I can think of are: a) Data only, or b) Actions only, or c) Data and actions. Isn't this sufficient to model any real world phnomena? By adding various contraints and relationships between such classes, you can describe anything. What do you have in mind that can't be described with classes? :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-03 0:00 ` Don Harrison @ 1996-05-07 0:00 ` Jon S Anthony 0 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-07 0:00 UTC (permalink / raw) In article <Dqt1K5.7Cx@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > :So what makes a function a "poor" abstraction??? In the right > :context (and the one under consideration is a good example), it > :can be the perfect abstraction. The real problem is, _all you > :have are classes_, and it just is plain not true that all the > :world's a class. > > The types of Eiffel (and probably other language's) classes that I > can think of are: > > a) Data only, or > b) Actions only, or > c) Data and actions. > > Isn't this sufficient to model any real world phnomena? By adding > various contraints and relationships between such classes, you can > describe anything. What do you have in mind that can't be described > with classes? This again hits upon that core issue that I discussed a bit in a reply to Joachim on this subject. I won't repeat that. However, it is not true that a class _is_ an operation. Neither _is_ it a "module". Sure, you can pound a square peg into a round hole and effectively use it as such, but that fact is getting at the point. /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] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Don Harrison ` (5 preceding siblings ...) 1996-04-30 0:00 ` Jon S Anthony @ 1996-04-30 0:00 ` Joachim Durchholz 1996-05-08 0:00 ` Joachim Durchholz 1996-05-10 0:00 ` Jon S Anthony 1996-05-02 0:00 ` Jon S Anthony 1996-05-06 0:00 ` Jon S Anthony 8 siblings, 2 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-04-30 0:00 UTC (permalink / raw) jsa@organon.com wrote 30.04.96 on Re: Real OO: > Well, whether it is a "real" problem or not, you have to introduce > contrived/extraneous classes in an attempt to define the things. Well, I can't avoid this if I equate module and class. In Ada, I'd have to create lots of extraneous packages - doesn't sound much of a difference to me. You always need some syntactic sugar if you want to add a new function as an afterthought - be it a class, package, module, or whatever. If you don't want syntactic sugar, use C. > > poor abstraction that got through because the programmer > So what makes a function a "poor" abstraction??? Use the definition that suits your taste :) . > In the right > context (and the one under consideration is a good example), it > can be the perfect abstraction. Of course. My point wasn't that functions as abstractions cannot be, it's just that *usually* functions are not the right tool. I don't think it is bad that everything is implemented as a class even though I fully recognize that > it just is plain not true that all the > world's a class. Still, classes are as good as any other concept for making a module, so why not use them? Usually, an abstraction isn't embodied in a single class, but in a set of related classes. While the notion of "class" is heavyweigth, actual classes are often quite lightweight. -Joachim -- Im speaking for myself here. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Joachim Durchholz @ 1996-05-08 0:00 ` Joachim Durchholz 1996-05-10 0:00 ` Jon S Anthony 1 sibling, 0 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-05-08 0:00 UTC (permalink / raw) jsa@organon.com wrote 08.05.96 on Re: Real OO: > > Well, it's the same with functions and routines. They were introduced > > to reuse code (rings a bell, doesn't it?). But nowadays I find myself > > using them just for naming a chunk of code. > > This is a blatant abuse of the concept of a subroutine; after all, I > > Why do you say that? I see no "abuse" of the concept at all. > Subprograms stand for a piece of functionality. That's not the original intent. Subprograms just happened to be useful for stuff the original inventors didn't consider. I think it's the same for classes; now people say "no, don't abuse classes for modules, that's a far too big gun at a small target". > Whether you use it > zero times, once, or a zillion times has no bearing on this. How this > is implmented also has no bearing on the _concept_. I am speaking > about what it is typically taken to stand for in a conceptual sense. In language usage, words have a tendency to acquire new concepts if they are halfways appropriate. This has happened with the concept of subroutine, and I expect it to happen with the concept of class. Of course less syntactic sugar for simple classes would be helpful. But I find myself using subroutines in spite of their clumsiness when it comes to just naming a piece of code, so I guess clumsiness doesn't really count in such a context. > Yes, I know. They are (typically) part of LACE (hmmm, if they are > actually called clusters, maybe the extra language environment that > the implementation is offering has to be called LACE...) And since > it is an add on, it is not as strong a notion and is YALL (yet another > little language)... Well, I think Bertrand has said what can be said about this. Clusters are a really central concept in BON. Lace is certainly YALL, but BON isn't... and Lace or an equivalent is part of BON. -Joachim -- Looking for a new job. Resume available on request. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-30 0:00 ` Joachim Durchholz 1996-05-08 0:00 ` Joachim Durchholz @ 1996-05-10 0:00 ` Jon S Anthony 1 sibling, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-10 0:00 UTC (permalink / raw) In article <68TAah0-3RB@herold.franken.de> jhd@herold.franken.de (Joachim Durchholz) writes: > > > Well, it's the same with functions and routines. They were introduced > > > to reuse code (rings a bell, doesn't it?). But nowadays I find myself > > > using them just for naming a chunk of code. > > > This is a blatant abuse of the concept of a subroutine; after all, I > > > > Why do you say that? I see no "abuse" of the concept at all. > > Subprograms stand for a piece of functionality. > > That's not the original intent. Subprograms just happened to be useful > for stuff the original inventors didn't consider. I think it's the > same for classes; now people say "no, don't abuse classes for modules, > that's a far too big gun at a small target". I really don't think the "original" intent about subprograms is relevant. As a concept they originated in "programming" and so if the meaning has drifted a _tiny_ bit where code duplication is not as central to the definition, it is not such a big deal. The idea of classes and taxonomic models OTOH is at least as old as Aristotle and I see no particular reason to abuse the notion - especially as class based OO purports to keep the old semantics of the notion central to its definition. I know you can pull a Humpty Dumpty here and make it be whatever you want, I just think that is very poor practice and adds to the general decline of communication. > In language usage, words have a tendency to acquire new concepts if > they are halfways appropriate. This has happened with the concept of > subroutine, and I expect it to happen with the concept of class. Actually, they can aquire new "meaning" (or, as is more often the case, _less_ meaning) simply by being commonly abused. I just think such abuse generally has an ill effect on how we communicate and often suckers neophytes (and even "experts") into confusion. At best, it "just" lowers the effectiveness and precision of language. A simple example is the "destruction" of the word "awesome". A sad case indeed. > > Yes, I know. They are (typically) part of LACE (hmmm, if they are > > actually called clusters, maybe the extra language environment that > > the implementation is offering has to be called LACE...) And since > > it is an add on, it is not as strong a notion and is YALL (yet another > > little language)... > > Well, I think Bertrand has said what can be said about this. Clusters > are a really central concept in BON. Lace is certainly YALL, but BON > isn't... and Lace or an equivalent is part of BON. Doesn't matter. BON is just YAN and no more integrated than Lace. /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] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Don Harrison ` (6 preceding siblings ...) 1996-04-30 0:00 ` Joachim Durchholz @ 1996-05-02 0:00 ` Jon S Anthony 1996-05-06 0:00 ` Jon S Anthony 8 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-02 0:00 UTC (permalink / raw) In article <4m6kcg$sa6@gaia.ns.utk.edu> mbk@caffeine.engr.utk.edu (Matt Kennel) writes: > : jsa@organon.com wrote 19.04.96 on Re: Real OO: > > : > Simply this: Classwide operations can be defined anywhere by anyone. > : > They do not have to be defined along with the primitive types in > : > their packages. > > And how is this different from Eiffel? > > class A is > > end > > class B is > > end > > class C is > > end > > > class ANYWHERE_BY_ANYONE is > > this_is_what_ada_calls_a_classwide_operation(a:A,b:B,c:C) is ... end; > > end Already covered this but you actually show what's different. You need to play some games 1. Create your extraneous/contrived class with the operation(s) 2. It can be (as you point out) used anywhere, even if that is not what you want (so you might start playing selective export games and getting even messier) 3. You have to use inheritance to use the thing (not exactly appropriate for most cases of the sort I was describing). There are probably others... /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] 218+ messages in thread
* Re: Real OO 1996-04-19 0:00 ` Don Harrison ` (7 preceding siblings ...) 1996-05-02 0:00 ` Jon S Anthony @ 1996-05-06 0:00 ` Jon S Anthony 8 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-06 0:00 UTC (permalink / raw) In article <67pyPs0-3RB@herold.franken.de> jhd@herold.franken.de (Joachim Durchholz) writes: > jsa@organon.com wrote 30.04.96 on Re: Real OO: > > > Well, whether it is a "real" problem or not, you have to introduce > > contrived/extraneous classes in an attempt to define the things. > > Well, I can't avoid this if I equate module and class. Right. > In Ada, I'd have to create lots of extraneous packages - doesn't > sound much of a difference to me. You always need some syntactic > sugar No, in Ada I can just write a function. Or procedure. I can also write a package if I think it makes sense. So, no, there is no similar requirement. Since classwide operations are not primitive, they do not need to part of a package. > > > poor abstraction that got through because the programmer > > So what makes a function a "poor" abstraction??? > > Use the definition that suits your taste :) . The one I use doesn't make it "poor" by definition! :-) Which means it can be perfectly appropriate alone at times. > > In the right > > context (and the one under consideration is a good example), it > > can be the perfect abstraction. > > Of course. My point wasn't that functions as abstractions cannot > be, it's just that *usually* functions are not the right tool. Seems like a reasonable view - and I didn't/don't disagree with it. > I don't think it is bad that everything is implemented as a class > even though I fully recognize that > > it just is plain not true that all the > > world's a class. > Still, classes are as good as any other concept for making a > module, so why not use them? Ahhh yes. This really sort of gets at this part of the issue. If you are going down the path of language design which basically tries to provide all that is necessary for a particular view of what it is to create "information structures and their operations" (in some circles aka programming...) with minimum required constructs and _acceptable_ inconvenience/distortion, then the above is clearly a reasonable view. It is even a good and laudable view. It is, in many respects, the view taken by Meyer in designing Eiffel. It is also not the only good and laudable view on such matters. The real problem with this view is simply that "programming" isn't really like mathematics (which clearly offers up the above sort of view in spades.) Now I like mathematics and this view a good deal - indeed, some of the more "pure" and esoteric nooks are what I am "officially" expert at. But software is much more like engineering than mathematics. And that means accepting certain constraints and realities which often do not fit the nice elegant models that would be preferable in a more "perfect" world. So, why not use classes for modules? Basically because that is not really their major semantic intent (not just in "programming" but in any taxonomic modeling scenario). Sure, you can say, "so what?", and pull a Humpty Dumpty and further say, "in this nook of the world, they do have this intent." Fine. But saying it's so doesn't make it so. And I haven't seen a convincing argument for this position (maybe there is one, and I just haven't seen it...) > Usually, an abstraction isn't embodied in a single class, but in > a set of related classes. While the notion of "class" is > heavyweigth, actual classes are often quite lightweight. Sounds like a subsystem. And it would be nice to have some construct with which you could express this collection of classes within the model that contains the particular notion of class with which you're working. A construct whose job it was to collect and organize sets of arbitrary sorts of things... /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] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Jon S Anthony @ 1996-04-08 0:00 ` Norman H. Cohen 1996-04-08 0:00 ` Robert A Duff ` (4 more replies) 1996-04-09 0:00 ` Valery CROIZIER 1996-04-09 0:00 ` Jon S Anthony 3 siblings, 5 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-04-08 0:00 UTC (permalink / raw) In article <DpBsG4.MIE@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: |> With such an appreciation of OO, you ought to be a big fan of Eiffel! I have nothing against Eiffel, and my background in formal methods makes me especially appreciative of the Eiffel approach to formalizing inheritance, but I find the Ada model of dispatching less error-prone and more flexible. Your remark betrays a provincial view, however--that Eiffel is the One True Answer for all those who want to write OO programs. Smart-aleck one-liners like |> BTW, I notice Ada people have a penchant for humungous identifiers. Is this |> an unconscious attempt to balance out the heavy syntax? ;-) only confirm this. You ought to open your mind and broaden your horizons. Let's try to return the discussion to technical arguments. |> :It's the "or a type that is a descendant of" that differentiates the |> :Eiffel approach from the Ada approach. |> |> ... and makes the Eiffel approach more flexible (and robust: Ada raises a |> Constraint_Error exception if the dynamic types of multiple controlling |> operands differ RM95 3.9.2 (16) ). What does Eiffel do in the case of a call like A.Corresponding_Parts_Equal(B) where A and B have different dynamic types, but the version of Corresponding_Parts_Equal for the dynamic type of A expects the parameter to be of (or descendended from) the dynamic type of Current? Some possible answers: (a) It is impossible to write a routine that has such expectations. [If so, Eiffel is less flexible than Ada.] (b) The Eiffel translator magically deduces an algorithm for Corresponding_Parts_Equal that works for the two dynamic types in question. [If so, we can stop this discussion now, I'm sold on Eiffel!] (c) A run-time error results immediately. [If so, no different from Ada.] (d) The version of Corresponding_Parts_Equal for the dynamic type of A is invoked with a parameter that does not have the expected dynamic type. Depending on the algorithm for that version of the routine, this may lead indirectly to a run-time error later, or fortuitously happen to give the right answer (at least for all the cases we tested so far ;-) ), or give an incorrect answer masquerading as a correct answer. [In any of these cases, Ada is more robust.] Please enlighten us. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-08 0:00 ` Norman H. Cohen @ 1996-04-08 0:00 ` Robert A Duff 1996-04-09 0:00 ` Norman H. Cohen 1996-04-10 0:00 ` Don Harrison ` (3 subsequent siblings) 4 siblings, 1 reply; 218+ messages in thread From: Robert A Duff @ 1996-04-08 0:00 UTC (permalink / raw) In article <4kbqun$iiv@watnews1.watson.ibm.com>, Norman H. Cohen <ncohen@watson.ibm.com> wrote: >Some possible answers: > >(a) It is impossible to write a routine that has such expectations. > [If so, Eiffel is less flexible than Ada.] >(b) The Eiffel translator magically deduces an algorithm for > Corresponding_Parts_Equal that works for the two dynamic types in > question. > [If so, we can stop this discussion now, I'm sold on Eiffel!] >(c) A run-time error results immediately. > [If so, no different from Ada.] >(d) The version of Corresponding_Parts_Equal for the dynamic type of A is > invoked with a parameter that does not have the expected dynamic > type. Depending on the algorithm for that version of the routine, > this may lead indirectly to a run-time error later, or fortuitously > happen to give the right answer (at least for all the cases we tested > so far ;-) ), or give an incorrect answer masquerading as a correct > answer. > [In any of these cases, Ada is more robust.] Norm, you didn't mention (e), something is done at link time. In Eiffel, there's something called "system validity check" or "system goodness check" or some such thing (I can't remember what it's called). It checks, at link time, certain things that *seem* to be holes in the type system, if one is used to thinking only about compile time. >Please enlighten us. Yes, please, perhaps our friends in comp.lang.eiffel would like to enlighten us about system (whatever) checks. Also, Eiffel compilers, unlike Ada compilers, typically support incremental compilation. How does this fit in? Does "link time" disappear, because it's all incremental? How does the system goodness check affect the ability to re-use stuff? - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-08 0:00 ` Robert A Duff @ 1996-04-09 0:00 ` Norman H. Cohen 0 siblings, 0 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-04-09 0:00 UTC (permalink / raw) In article <DpKDqC.MB6@world.std.com>, bobduff@world.std.com (Robert A Duff) writes: |> Norm, you didn't mention (e), something is done at link time. I had intended |> >(a) It is impossible to write a routine that has such expectations. |> > [If so, Eiffel is less flexible than Ada.] to include any pre-execution-time prohibition, whether by a compile-time or a link-time check. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-08 0:00 ` Norman H. Cohen 1996-04-08 0:00 ` Robert A Duff @ 1996-04-10 0:00 ` Don Harrison 1996-04-11 0:00 ` Jacob Gore ` (2 subsequent siblings) 4 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-04-10 0:00 UTC (permalink / raw) Norman H. Cohen writes: :In article <DpBsG4.MIE@assip.csasyd.oz>, donh@syd.csa.com.au (Don :Harrison) writes: [Norman's chastisement of me omitted] :Please enlighten us. ... he says, wheeling the heavy artillery into position. I'll try to once I've cleared up my own misunderstandings :-) but someone else will probably lose patience and do it for me. :-- :Norman H. Cohen ncohen@watson.ibm.com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-08 0:00 ` Norman H. Cohen 1996-04-08 0:00 ` Robert A Duff 1996-04-10 0:00 ` Don Harrison @ 1996-04-11 0:00 ` Jacob Gore 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Don Harrison 4 siblings, 0 replies; 218+ messages in thread From: Jacob Gore @ 1996-04-11 0:00 UTC (permalink / raw) Norman H. Cohen writes > What does Eiffel do in the case of a call like > A.Corresponding_Parts_Equal(B) where A and B have different dynamic > types, but the version of Corresponding_Parts_Equal for the dynamic type > of A expects the parameter to be of (or descendended from) the dynamic > type of Current? You would declare the function corresponding_parts_equal as follows: corresponding_parts_equal(other: like Current): BOOLEAN is ... This can appear in the text of A's class, or in the text of a class that A's class inherits (directly or indirectly). "other: like Current" means that the static type of `other' is Current's (in your example, A's) class. For example, suppose class Z inherits from class Y, which inherits from class X, where the function corresponding_parts_equal is defined (and neither Y nor Z redefine it). The above declaration, as inherited by Z, is shorthand for the redefinition corresponding_parts_equal(other: Z): BOOLEAN is ... same body. That means that `other' can be an object of class Z or any of its subclasses. This does what you asked, doesn't it? Jacob --- Jacob Gore, Eastern NM U. Jacob.Gore@ENMU.Edu | Jacob@ToolCASE.Com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-08 0:00 ` Norman H. Cohen ` (2 preceding siblings ...) 1996-04-11 0:00 ` Jacob Gore @ 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Jacob Gore 1996-04-12 0:00 ` Don Harrison 4 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-04-12 0:00 UTC (permalink / raw) Norman H. Cohen writes: :In article <DpBsG4.MIE@assip.csasyd.oz>, donh@syd.csa.com.au (Don :Harrison) writes: [...] :Let's try to return the discussion to technical arguments. : :|> :It's the "or a type that is a descendant of" that differentiates the :|> :Eiffel approach from the Ada approach. I must confess to still being in the woods over whether: corresponding_parts_equal (other: like Current): BOOLEAN is ... means that, in a call x.corresponding_parts_equal (y): a) the STATIC type of y must conform to the STATIC type of x, or b) the DYNAMIC type of y must conform to the DYNAMIC type of x ('conform to' meaning having the same (or more specific) type). ETL seems to suggest a): [22.9 - The call validity rule]. This says: "... if x is anchored to another final argument, as in r (z:T; x: like z) is ... then y of type YT conforms to x in a call r(u,y) if u is of type UT (conforming to T) and YT conforms to UT.". I assume this is talking about static conformance. Also, [13.8 - Expression conformance] gives the example: "x: CT; y: DT; ... if equal (x,y) then ...". I haven't been able to find anything about dynamic conformance. As you imply, a call in which the STATIC types of the actual parameters conform but the DYNAMIC types do not is a possible source of difficulty. How about swapping the order of parameters: frozen corresponding_parts_equal (other: like Current): BOOLEAN is do if not other.conforms_to (Current) then -- inherited from ANY Result := other.parts_equal (Current) -- dispatch else Result := parts_equal (other) -- dispatch end end parts_equal (other: like Current): BOOLEAN is do ... end Would you use a classwide operation in Ada to do the same trick? ... and get around the following problem?: :|> ... and makes the Eiffel approach more flexible (and robust: Ada raises a :|> Constraint_Error exception if the dynamic types of multiple controlling :|> operands differ RM95 3.9.2 (16) ). Note that the symmetry of multiple controlling operands may exist statically but may be non-existent dynamically. To acheive something that works in this situation, you have to redefine the operation as classwide anyway. Such symmetry that only exists in the special case of all actual parameters having the same dynamic type is of questionable value, IMO. :What does Eiffel do in the case of a call like :A.Corresponding_Parts_Equal(B) where A and B have different dynamic :types, but the version of Corresponding_Parts_Equal for the dynamic type :of A expects the parameter to be of (or descendended from) the dynamic :type of Current? Assuming you mean 'where the dynamic type of A conforms to the dynamic type of B' (rather than the reverse): :Some possible answers: : :(a) It is impossible to write a routine that has such expectations. : [If so, Eiffel is less flexible than Ada.] No. :(b) The Eiffel translator magically deduces an algorithm for : Corresponding_Parts_Equal that works for the two dynamic types in : question. : [If so, we can stop this discussion now, I'm sold on Eiffel!] I wish! :(c) A run-time error results immediately. : [If so, no different from Ada.] Don't think so. :(d) The version of Corresponding_Parts_Equal for the dynamic type of A is : invoked with a parameter that does not have the expected dynamic : type. Depending on the algorithm for that version of the routine, : this may lead indirectly to a run-time error later, or fortuitously : happen to give the right answer (at least for all the cases we tested : so far ;-) ), or give an incorrect answer masquerading as a correct : answer. : [In any of these cases, Ada is more robust.] Not quite. As shown above, a correctly designed algorithm will give the correct result in all circumstances. :Please enlighten us. I hope someone will enlighten me about whether my interpretation of ETL is correct. :-- :Norman H. Cohen ncohen@watson.ibm.com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Don Harrison @ 1996-04-12 0:00 ` Jacob Gore 1996-04-16 0:00 ` Don Harrison 0 siblings, 1 reply; 218+ messages in thread From: Jacob Gore @ 1996-04-12 0:00 UTC (permalink / raw) Don Harrison writes > I must confess to still being in the woods over whether: > > corresponding_parts_equal (other: like Current): BOOLEAN is ... > > means that, in a call x.corresponding_parts_equal (y): > > a) the STATIC type of y must conform to the STATIC type of x, or > b) the DYNAMIC type of y must conform to the DYNAMIC type of x > > ('conform to' meaning having the same (or more specific) type). Let me try to answer this question while avoiding this terminology for a while. Suppose the declaration of class PPP contains the declarations x: AAA; y: like x; The first line means "x can refer to an object in class AAA, or an object in any subclass of AA". No confusion there, right? Simple o-o stuff. The second line means "the rules that are applicable to y in this class are the same as the rules that are applicable to x in this class." The words to note are "in this class". They refer to "the flat form" of the class: all the code introduced in this class, plus all the code that is inherited but not redefined. That definition is simple, and includes the answer to your question. However, if you are trying to decide whether that means "of same type as the variable x" ("static type") vs. "in the same class as the object to which x refers" ("dynamic type"), please observe that it does not mean either. Consider an example. Suppose class QQQ inherits from PPP. If it redefines x as x: BBB; (which is legal as long as BBB is a descendant of AAA), then y, as an object in class QQQ sees it, may refer only to an object in class BBB or a subclass of BBB. On the other hand, if QQQ just inherits x (so it is still "x: AAA"), then y may refer to an object in class AAA or in a subclass of AAA. The same rules apply to y as to x -- as an object in class QQQ sees y and x. So what does that mean for the declaration y: like Current (which was the original question)? Conceptually, Current is always redefined to the most specific type. In class AAA, Current is of type AAA, in class BBB, it is of type BBB, etc. So "y: like Current" means that y can only be an object in the same class as the object that is executing the given code. So in the case of "like Current", you do get the "dynamic type". But only because the "static type" of Current is always the same as its "dynamic type." In general, "like x" means "the `static type' of x, from the point of view of the object referred to by Current." Hope this gets you through the woods :-) Jacob --- Jacob Gore, Eastern NM U. Jacob.Gore@ENMU.Edu | Jacob@ToolCASE.Com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Jacob Gore @ 1996-04-16 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-04-16 0:00 UTC (permalink / raw) Jacob Gore writes: [...] Please be patient with me; I'm a slow learner. : y: like x; The static type is the static type of x in the class where this definition appears (explicitly, or implicitly in a descendant which does not redefine y). Now, for dynamic type do we have: a) The dynamic type is the same as the dynamic of x, or b) The dynamic type is the same as the dynamic of x (or a descendant of that type)? [a) seems more sensible, IMO]. [...] :: y: like Current The static type is the type of the class where this definition appears (explicitly, or implicitly in a descendant which does not redefine y). The dynamic type is the same as that of the current object (but not a descendant of that type). :Hope this gets you through the woods :-) Almost. :Jacob :--- :Jacob Gore, Eastern NM U. Jacob.Gore@ENMU.Edu | Jacob@ToolCASE.Com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-08 0:00 ` Norman H. Cohen ` (3 preceding siblings ...) 1996-04-12 0:00 ` Don Harrison @ 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Matt Kennel ` (2 more replies) 4 siblings, 3 replies; 218+ messages in thread From: Don Harrison @ 1996-04-12 0:00 UTC (permalink / raw) Norman writes: :In article <DpBsG4.MIE@assip.csasyd.oz>, donh@syd.csa.com.au (Don :Harrison) writes: : :|> With such an appreciation of OO, you ought to be a big fan of Eiffel! : :I have nothing against Eiffel, and my background in formal methods makes :me especially appreciative of the Eiffel approach to formalizing :inheritance, That's nice to hear. : but I find the Ada model of dispatching less error-prone and :more flexible. Not sure how you arrive at this. :Your remark betrays a provincial view, however--that Eiffel is the One :True Answer for all those who want to write OO programs. Smart-aleck :one-liners like : :|> BTW, I notice Ada people have a penchant for humungous identifiers. Is this :|> an unconscious attempt to balance out the heavy syntax? ;-) : :only confirm this. Sorry for being impolite. : You ought to open your mind and broaden your :horizons. Yes, I confess my knowledge of OO languages is fairly narrow. I guess I'm very selective about which I bother looking into especially when I know how well designed Eiffel is. Some claim that Sather is good and I would like to know more about it but, from my understanding, it has no assertions and has (or is about to have) a separate module mechanism. If this is true, it seems likely the designers have missed the importance of assertions and uni-encapsulation in an OO model. Don't think that I advocate these qualities because they exist in Eiffel. Rather, I advocate Eiffel because it's design shows that Bertrand Meyer has recognized the importance of these (and other) characteristics in an OO model. I understand his frustration at the refusal of the vast majority of the OO community to acknowledge that these elements are important. The pure and simple fact is that they have missed it and he has got it right. You may think that I've just embraced these ideas because I like Eiffel so I'm just prepared to blindly accept whatever Bertrand says is important. That is not true. Before I started this thread, I was aware of what elements he considered essential (although I had forgotten some of them) and had not read about them in OOSC or elsewhere. I sought to determine by my own reasoning whether they were essential, preferable or didn't matter and independently came to the conclusion that to maximise reusability and reliability, they WERE essential. To accept anything less is to diminish the efficacy of the OO paradigm. I was encouraged a couple of weeks ago when I perused a copy of OOSC in a bookshop (to find out about function 'dynamic_type') and found that my thinking was close to Bertrand's. I think the problem with many people is they fail to recognise that the OO paradigm is fundamentally different from the traditional approach so they tack inheritance and polymorphism onto a traditional language base and think they have OO. The real power of OO is it's ability to maximise reuse and reliability. These things are probably realised to their full potential in Eiffel because the elements of OO that are essential to acheive those goals are present in the design. It amazes me that so many experienced software people acknowledge that Eiffel is better designed than most OO languages but in the next breath claim that the elements that make it distinctive are not necessary. The reason Eiffel is as good as it is is BECAUSE it has these features (eg. uni-encapsulation, inheritance, polymophism, assertions, GC, static typing, MI). Other languages have some of them but Eiffel has all of them. I think that Ada95 is a vast improvement over Ada83. But Ada with real OO would be much better than Ada95. And Eiffel is much better than both. Yes, I am narrow minded but have good cause to be! [...] :-- :Norman H. Cohen ncohen@watson.ibm.com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Don Harrison @ 1996-04-12 0:00 ` Matt Kennel 1996-04-15 0:00 ` Don Harrison 1996-04-12 0:00 ` Jon S Anthony 1996-04-16 0:00 ` Jon S Anthony 2 siblings, 1 reply; 218+ messages in thread From: Matt Kennel @ 1996-04-12 0:00 UTC (permalink / raw) Don Harrison (donh@syd.csa.com.au) wrote: : Yes, I confess my knowledge of OO languages is fairly narrow. I guess I'm very : selective about which I bother looking into especially when I know how well : designed Eiffel is. Some claim that Sather is good and I would like to know : more about it but, from my understanding, it has no assertions and has (or is : about to have) a separate module mechanism. It does have pre/post and invariants stolen shamelessly from Eiffel, in addition to Ada-like 'in', 'out' and 'inout' parameter modes which are vaguely preconditionish. (This made iters and interfacing to Fortran more natural and orthogonal). It doesn't yet have them in abstract supertypes; there was some original trepidation because of some technical problems (not in implementation but in undesirable consequences) but I think they may be in the upcoming 1.1 release. Note that they are 'inherited' through implementation inclusion, so you already get a large fraction of the Eiffel benefits. : If this is true, it seems likely : the designers have missed the importance of assertions and uni-encapsulation : in an OO model. No. Sather's locus of encapsulation is the class, exactly as Eiffel. It has a privileged distinguished dispatching member, just like Eiffel (but it does have overloading unlike Eiffel). Sather is much more like Eiffel than you believe. Differences include: (only the first two are major) * distinguishes implementation inheritance from interface subtyping (Yeah the thing that Java does). This makes feature inheritance and renaming a bit simpler. * special iterator constructs (very cool!) * Allows post-hoc supertyping. (put a new abstract class I wrote 'over' existing classes, including library classes) * different kind of type inference (more limited than 'like', but allows automatic declaration at first assignment) * feature overloading based on static argument types * more limited range of definable infix operators * no class specific export controls * parameter modes * 'partial classes' and 'stubs' --- enforced mixins for implementation inheirtance * user-definable [] -- left hand assignment distinguished from right hand fetch. * differences in interfacing with external languages, C and Fortran. The supposed module mechanism is quite lightweight and is *not* in the language. It's something peculiar to the compiler for ease of use. Sather designers recognize the eventual need for some very large scale organizing mechanism but don't think that the common notion of 'modules' are good enough. There were a flurry of proposals a little over a year ago on the internal mailing list, but no satisfactory agreement, except that other issues are more immediately pressing. Personally I don't feel that a "boxes in boxes" traditional hierarchical approach is the right concept. We're on the lookout for something simple, clear, powerful and brilliant. cheers matt http://www.icsi.berkeley.edu/~sather ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Matt Kennel @ 1996-04-15 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-04-15 0:00 UTC (permalink / raw) Matt Kennel writes: [stuff about how Sather compares with Eiffel] Thanks for the correction and clarification. Sounds interesting. Will have a look at it sometime. :cheers :matt Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Matt Kennel @ 1996-04-12 0:00 ` Jon S Anthony 1996-04-13 0:00 ` Robert A Duff 1996-04-16 0:00 ` Jon S Anthony 2 siblings, 1 reply; 218+ messages in thread From: Jon S Anthony @ 1996-04-12 0:00 UTC (permalink / raw) In article <Dpq3pF.5sr@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > Yes, I confess my knowledge of OO languages is fairly narrow. I > guess I'm very Yes. It shows. > selective about which I bother looking into especially when I know how well > designed Eiffel is. Some claim that Sather is good and I would like to know > more about it but, from my understanding, it has no assertions and has (or is > about to have) a separate module mechanism. If this is true, it seems likely > the designers have missed the importance of assertions and uni-encapsulation > in an OO model. Nah. They just know a _whole_ lot more about this stuff than you. Sadly, given your self proclaimed narrow mindedness, they probably know more than you could even hope to know. BTW, your understanding is wrong - Sather has per method pre and post conditions, class wide invariants, and the assert statement. > community to acknowledge that these elements are important. The pure > and simple fact is that they have missed it and he has got it right. Yeah, yeah, yeah. We already know you are closed minded dogmatic evangelist. Meyer indeed has a lot of "right" things to say. This is not the same as saying that everything he says is right. So the pure and simple fact is, you are in the weeds. > Yes, I am narrow minded but have good cause to be! Pssst, this is an oxymoron. /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] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Jon S Anthony @ 1996-04-13 0:00 ` Robert A Duff 0 siblings, 0 replies; 218+ messages in thread From: Robert A Duff @ 1996-04-13 0:00 UTC (permalink / raw) Now, now. No need to get nasty. In article <JSA.96Apr12172019@organon.com>, Jon S Anthony <jsa@organon.com> wrote: >In article <Dpq3pF.5sr@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > >> Yes, I confess my knowledge of OO languages is fairly narrow. I >> guess I'm very > >Yes. It shows. ... >Nah. They just know a _whole_ lot more about this stuff than you. >Sadly, given your self proclaimed narrow mindedness, they probably >know more than you could even hope to know. ... >Yeah, yeah, yeah. We already know you are closed minded dogmatic >evangelist. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Matt Kennel 1996-04-12 0:00 ` Jon S Anthony @ 1996-04-16 0:00 ` Jon S Anthony 2 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-04-16 0:00 UTC (permalink / raw) In article <DpryG4.Hx8@world.std.com> bobduff@world.std.com (Robert A Duff) writes: > Now, now. No need to get nasty. Of course you are correct. Sorry. /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] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Jon S Anthony 1996-04-08 0:00 ` Norman H. Cohen @ 1996-04-09 0:00 ` Valery CROIZIER 1996-04-09 0:00 ` Jon S Anthony 3 siblings, 0 replies; 218+ messages in thread From: Valery CROIZIER @ 1996-04-09 0:00 UTC (permalink / raw) Norman H. Cohen writes > (a) It is impossible to write a routine that has such expectations. > [If so, Eiffel is less flexible than Ada.] > [...] > (c) A run-time error results immediately. > [If so, no different from Ada.] > [...] Unfortunately, Eiffel is not flexible enough to allow so dangerous a statement (Note: the *statement* is illegal, not the routine definition) and your program won't have the pleasure of crashing at run time. As a conclusion, if you prefer run time errors to compile time errors, Eiffel is not for you. -- Valery ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison ` (2 preceding siblings ...) 1996-04-09 0:00 ` Valery CROIZIER @ 1996-04-09 0:00 ` Jon S Anthony 3 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-04-09 0:00 UTC (permalink / raw) In article <4kbqun$iiv@watnews1.watson.ibm.com> ncohen@watson.ibm.com (Norman H. Cohen) writes: > What does Eiffel do in the case of a call like > A.Corresponding_Parts_Equal(B) where A and B have different dynamic > types, but the version of Corresponding_Parts_Equal for the dynamic type > of A expects the parameter to be of (or descendended from) the dynamic > type of Current? > > Some possible answers: > > (a) It is impossible to write a routine that has such expectations. > [If so, Eiffel is less flexible than Ada.] > (b) The Eiffel translator magically deduces an algorithm for > Corresponding_Parts_Equal that works for the two dynamic types in > question. > [If so, we can stop this discussion now, I'm sold on Eiffel!] > (c) A run-time error results immediately. > [If so, no different from Ada.] > (d) The version of Corresponding_Parts_Equal for the dynamic type of A is > invoked with a parameter that does not have the expected dynamic > type. Depending on the algorithm for that version of the routine, > this may lead indirectly to a run-time error later, or fortuitously > happen to give the right answer (at least for all the cases we tested > so far ;-) ), or give an incorrect answer masquerading as a correct > answer. > [In any of these cases, Ada is more robust.] > > Please enlighten us. You forgot about the Eiffel system validity check - a link time thing. See ETL 22.4-22.9. 22.9 in particular has a lot of info for an implementor. Unfortunately as is known in Eiffel circles this is a rather notorious area and many implementations just "give up"... /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] 218+ messages in thread
* Re: Real OO 1996-03-26 0:00 ` Norman H. Cohen 1996-04-04 0:00 ` Don Harrison @ 1996-04-09 0:00 ` Joachim Durchholz 1996-05-02 0:00 ` Joachim Durchholz 1996-05-07 0:00 ` Joachim Durchholz 3 siblings, 0 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-04-09 0:00 UTC (permalink / raw) bobduff@world.std.com wrote 08.04.96 on Re: Real OO: > In Eiffel, there's something called "system validity check" or "system > goodness check" or some such thing (I can't remember what it's called). It is "system validity". It is a check wether a call to a routine with a redefined parameter can result in an object being mistaken for a descendant one. Such systems (programs) are not system- valid. The notion "system validity" is problematic in itself, because it can make sets of modules invalid just by combining them. Bertrand Meyer has proposed a different rule that is a bit more restrictive than the current definition of system validity, but which can be checked on a by-class level. Details are on http://www.eiffel.com, in a paper called "Beware of polymorphic catcalls". Anyway, I'm not so sure something like congruency can be naturally done in Eiffel. Eiffel deliberately lacks mechanisms for polymorphism in routine parameters (apart from the usual polymorphism in the implicit Current or this or self or however- your-language-names-it parameter). My OOA/OOD "bible" says this can always be achieved by something like muli-polymorphic(parm) begin parm.function() end but I don't think this applies to congruency or other symmetric polymorphic functions. I can't comment on this without some serious thinking though (and I've been trying to avoid that for the past holidays <g>). -Joachim -- Im speaking for myself here. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-26 0:00 ` Norman H. Cohen 1996-04-04 0:00 ` Don Harrison 1996-04-09 0:00 ` Joachim Durchholz @ 1996-05-02 0:00 ` Joachim Durchholz 1996-05-05 0:00 ` Robert A Duff ` (2 more replies) 1996-05-07 0:00 ` Joachim Durchholz 3 siblings, 3 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-05-02 0:00 UTC (permalink / raw) ncohen@watson.ibm.com wrote 08.04.96 on Re: Real OO: > I have nothing against Eiffel, and my background in formal methods makes > me especially appreciative of the Eiffel approach to formalizing > inheritance, but I find the Ada model of dispatching less error-prone and > more flexible. As I understand it, you mean the multiple dispatching mechanisms (a.k.a. classwide operations). While I agree that these can be useful, I noted a few problems that can arise in this context: 1) Ambiguity of routine to be called. Consider the following situation: I have two classes A and B that each have a descendant A' and B', respectively. Now let's define a class-wide function (just class names in the parameter list) f(A, B) meaning whenever f is called, parameters my be of class A and its descendants resp. B and its descendants. When I write an additional function f(A', B) -- redefinition 1 any calls to f that have their first parameter of class A' (or further down the inheritance chain) will get to call this version of f. Likewise, a function f(A, B') -- redefition 2 will do the same for B'. But which of these gets called if the parameters are of type A' *and* B'? Both functions are applicable, redefinition 1 and redefinition 2. You can add disambiguating rules, of course, like giving precedence to the first parameter. But this trades ambiguity for obscureness. Imagine a developer who has introduced redefinition 2, tested it, and put to productive use. Later (read "years later"), a different programmer introduces redefinition 1, which will override redefinition 2. So suddenly lots of code will have a different meaning, at places that I suspect can't easily be spotted. I.e. this tightens the coupling between two classes. 2) Combinatorial explosion. The number of possible class-wide functions is (number of descendants of A) times (number of descendants of B) ("descendant" including the class itself here). This explosion may not be visible in the source code; however, this appearance is deceiving. After all, even if there is no code necessary for most of the combinations, you have to check *all* of the combinations and look wether an overriding class-wide function is necessary. Even worse, classes cannot be viewd as independent entities anymore. Whenever a new class is added, all combinations with all classes for wich a multiply dispatching routine is defined have to be reviewed. [Sarcasm (sorry) on] I consider this a good scenario for a horror story... but not for software development. [Sarcasm off] 3) It seems unnecessary. Some of the Design Patterns explicitly address such combinatorial explosions and give a solution. None of the solution employs multiple dispatching. (This may be more due to the fact that it evolved from single-dispatching language tradition, of course.) Conclusion I'm not expert enough to actually determine wether the available design patterns will work in every case. However, the combinatorial explosion effect makes me more than reluctant to accept multiple dispatching as a desirable feature, even if it is useful in special cases. And I suspect any solution that controls this explosion also opens an avenue to designing a single-inheritance class hierarchy. -Joachim -- Im speaking for myself here. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Joachim Durchholz @ 1996-05-05 0:00 ` Robert A Duff 1996-05-05 0:00 ` Robert Dewar 1996-05-06 0:00 ` Norman H. Cohen 1996-05-07 0:00 ` Real OO Amit Patel 2 siblings, 1 reply; 218+ messages in thread From: Robert A Duff @ 1996-05-05 0:00 UTC (permalink / raw) In article <6850x6pV3RB@herold.franken.de>, Joachim Durchholz <jhd@herold.franken.de> wrote: >ncohen@watson.ibm.com wrote 08.04.96 on Re: Real OO: >> I have nothing against Eiffel, and my background in formal methods makes >> me especially appreciative of the Eiffel approach to formalizing >> inheritance, but I find the Ada model of dispatching less error-prone and >> more flexible. > >As I understand it, you mean the multiple dispatching mechanisms >(a.k.a. classwide operations). >[...several reasons why multiple dispatching is evil] No, Ada does *not* support multiple dispatching. Class-wide operations are something different. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-05 0:00 ` Robert A Duff @ 1996-05-05 0:00 ` Robert Dewar 0 siblings, 0 replies; 218+ messages in thread From: Robert Dewar @ 1996-05-05 0:00 UTC (permalink / raw) Bob Duff said "No, Ada does *not* support multiple dispatching. Class-wide operations are something different." That's a little confusing. Multiple dispatching, like multiple inheritance, is a programming paradigm. One can follow this paradigm either by using built-in features in the lanuage which directly support the required semantics, or it can be programmed using features that are appropriate. It is in the latter sense that class-wide programming has something to do with multiple dispatching. If you need MD, then you would use CW operations to achieve the desired effect. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Joachim Durchholz 1996-05-05 0:00 ` Robert A Duff @ 1996-05-06 0:00 ` Norman H. Cohen 1996-05-07 0:00 ` Don Harrison 1996-05-07 0:00 ` Ada terminology (was Re: Real OO) David Hopwood 1996-05-07 0:00 ` Real OO Amit Patel 2 siblings, 2 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-05-06 0:00 UTC (permalink / raw) In article <6850x6pV3RB@herold.franken.de>, jhd@herold.franken.de (Joachim Durchholz) writes: |> As I understand it, you mean the multiple dispatching mechanisms |> (a.k.a. classwide operations). No, that's not what classwide operatins are. Your post reflects a number of misconceptions. |> While I agree that these can be useful, I noted a few problems |> that can arise in this context: |> |> 1) Ambiguity of routine to be called. |> Consider the following situation: I have two classes A and B that |> each have a descendant A' and B', respectively. |> Now let's define a class-wide function (just class names in the |> parameter list) |> f(A, B) |> meaning whenever f is called, parameters my be of class A and its |> descendants resp. B and its descendants. What you're describing here is impossible in Ada. It's hard to figure out what you really mean because you are using the word "class" in its Eiffel sense rather than it's Ada sense to describe an Ada example. In Ada, a class is not a type, but a SET OF TYPES. In particular, the set of tagged types directly or indirectly derived from a common root forms a "derivation class". ("Deriving" from a type means defining a descendant type.) For each type T at the root of a tree or subtree in the derivation hierarchy, there is a "classwide type", T'Class, whose values are taken from the discriminated union of the types in the class. One cannot derive from a classwide type. (By "discriminated union," we mean that for each value V of each type in the class, there is a corresponding value in T'Class, and that the corresponding value in T'Class includes a "tag" identifying the type of the original value V.) An Ada function parameter is declared to belong to a particular type, not a particular class, so if there is a function declared to take an A parameter and a B parameter, A and B must be types, not classes. Let us suppose that A and B are ordinary tagged types, NOT classwide types (so that A'Class and B'Class are the corresponding classwide types). If you write a procedure procedure P(X: in A); in the same package in which A is declared, then there is a version of P (either inherited or explicitly overridden) in each type descended from A. P can be called with an actual parameter of type A'Class, in which case the call dispatches to the appropriate version of P based on the tag of the actual parameter. If you write a procedure procedure Q(X: in A'Class); it is not an operation of type A, so it is not inherited in the usual sense by types derived from A. However, in a call, the actual parameter may belong to A'Class, A, or any type descended from A. Unlike P, Q never dispatches. No matter what the tag of the actual parameter is, Q always executes the same procedure body. The body of Q must be written in such a way that it depends only on properties common to all types in the class. (Ada rules allow this to be enforced at compile time.) Now suppose A and B are declared in the same package. It is illegal to declare procedure R(X: in A; Y: in B); --which would appear to be a procedure that dispatches based on both the tag of X (identifying a type in the class rooted at A) and the tag of Y (identifying a type in the class rooted at B). THERE IS NO CLOS-LIKE MULTIPLE DISPATCH IN ADA. You CAN declare procedure S(X: in A'Class; Y: in B'Class); and this poses no problems. It does not dispatch. It can take a first parameter belonging to A'Class, A, or any descendant of A along with a second parameter belonging to B'Class, B, or any descendent of B. It always invokes the same procedure body, which must be written to exploit only those properties common to all types in the class rooted at A (for the first parameter) and those properties common to all types in the class rooted at B (for the second parameter). It is also possible to write procedure T(X:in A; Y: in B'Class); which dispatches based on the tag of X to the appropriate version of T (inherited by descendants of A but not by descendants of B). For their manipulations of the second parameter, all the versions of T exploit only those properties common to all types in the class rooted at B. The procedure procedure S(X: in A'Class; Y: in B); is analogous. |> When I write an additional function |> f(A', B) -- redefinition 1 |> any calls to f that have their first parameter of class A' (or |> further down the inheritance chain) will get to call this version |> of f. |> Likewise, a function |> f(A, B') -- redefition 2 |> will do the same for B'. |> But which of these gets called if the parameters are of type A' |> *and* B'? Both functions are applicable, redefinition 1 and |> redefinition 2. As noted above, this situation does not arise in Ada. A function cannot be a dispatching function of two different types. |> 2) Combinatorial explosion. |> |> The number of possible class-wide functions is (number of |> descendants of A) times (number of descendants of B) |> ("descendant" including the class itself here). |> |> This explosion may not be visible in the source code; however, |> this appearance is deceiving. After all, even if there is no code |> necessary for most of the combinations, you have to check *all* |> of the combinations and look wether an overriding class-wide |> function is necessary. Several misconceptions here. First, classwide subprograms have parameters belonging to classwide types, which cannot be derived from, so classwide subprograms are never overridden. Second, unlike dispatching subprograms, which are written to handle the distinctions among different types in a class, classwide subprograms are written to handle things that are common to all types in the class. For each class (i.e., set of types) precisely one version of a classwide subprogram is necessary. That's where the name "classwide" comes from--one version applies to all types in the class. |> However, the combinatorial explosion effect makes me more than |> reluctant to accept multiple dispatching as a desirable feature, |> even if it is useful in special cases. Quite possibly, but you should address that objection to advocates of CLOS. Ada does not have the sort of multiple dispatching mechanism you envision. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-06 0:00 ` Norman H. Cohen @ 1996-05-07 0:00 ` Don Harrison 1996-05-07 0:00 ` Jon S Anthony 1996-05-08 0:00 ` Norman H. Cohen 1996-05-07 0:00 ` Ada terminology (was Re: Real OO) David Hopwood 1 sibling, 2 replies; 218+ messages in thread From: Don Harrison @ 1996-05-07 0:00 UTC (permalink / raw) Norman H. Cohen lectures Joachim (but in doing so provides a good summary of classwide operations): :In article <6850x6pV3RB@herold.franken.de>, :jhd@herold.franken.de (Joachim Durchholz) writes: : :|> As I understand it, you mean the multiple dispatching mechanisms :|> (a.k.a. classwide operations). : :No, that's not what classwide operatins are. Your post reflects a number :of misconceptions. Just one: that classwide operations are multiply dispatching. [...] Translating the examples into equivalent Eiffel (my additions in brackets), :If you write a procedure [in the same interface as A] : : procedure P(X: in A); class A feature p is ... -- X = Current :... If you write a procedure [anywhere] : : procedure Q(X: in A'Class); class A feature frozen q is ... -- X = Current, or class SOME_OTHER_CLASS feature q (x: A) is ... : procedure R(X: in A; Y: in B); [where A, B and R are in same interface] (It's syntactically impossible in Eiffel for an operation to be declared in two different classes). :--which would appear to be a procedure that dispatches based on both the :tag of X (identifying a type in the class rooted at A) and the tag of Y :(identifying a type in the class rooted at B). THERE IS NO CLOS-LIKE :MULTIPLE DISPATCH IN ADA. You CAN declare : : procedure S(X: in A'Class; Y: in B'Class); [anywhere] class A feature frozen q (y: B) is ... -- X = Current, or class B feature frozen q (x: X) is ... -- Y = Current, or class SOME_OTHER_CLASS feature frozen q (x: X; y: B) is ... [...] :It is also possible to write [in the same interface as A] : : procedure T(X:in A; Y: in B'Class); The addition of 'in' protects X from update. ... class SOME_OTHER_CLASS feature frozen q (x: X; y: B) is do ... ensure x = old x y = old y end [...] As the equivalent Eiffel shows, the term 'classwide operation' is misleading. An operation may be dispatching WRT one parameter but classwide WRT another. It is more accurate to speak of classwide operands rather than classwide operations. :-- :Norman H. Cohen ncohen@watson.ibm.com -- /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-07 0:00 ` Don Harrison @ 1996-05-07 0:00 ` Jon S Anthony 1996-05-08 0:00 ` Don Harrison 1996-05-08 0:00 ` Norman H. Cohen 1 sibling, 1 reply; 218+ messages in thread From: Jon S Anthony @ 1996-05-07 0:00 UTC (permalink / raw) In article <Dr0rp1.1Ks@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: [Whole bunch of stuff...] > As the equivalent Eiffel shows, the term 'classwide operation' is misleading. > An operation may be dispatching WRT one parameter but classwide WRT another. > It is more accurate to speak of classwide operands rather than classwide > operations. I'm not clear on why you bring the Eiffel in to support your claim that the term "classwide operation" is misleading. In fact, the Eiffel seems completely irrelevant. There _is_ a (small) problem with the terminology here as you are indicating - specifically when there are mixtures of specific type and classwide type formals in an operation's signature. However, it does seem perfectly clear to say O is a classwide operation when only classwide formals are given. For example, procedure O1 (X: A'Class); function O2 (X: A'Class; Y: B'Class) return C'Class; ... /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] 218+ messages in thread
* Re: Real OO 1996-05-07 0:00 ` Jon S Anthony @ 1996-05-08 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-08 0:00 UTC (permalink / raw) Jon S Anthony writes: :In article <Dr0rp1.1Ks@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: : :[Whole bunch of stuff...] : :> As the equivalent Eiffel shows, the term 'classwide operation' is misleading. :> An operation may be dispatching WRT one parameter but classwide WRT another. :> It is more accurate to speak of classwide operands rather than classwide :> operations. : :I'm not clear on why you bring the Eiffel in to support your claim that :the term "classwide operation" is misleading. The examples were primarily for the benefit of anyone still confused about what 'classwide' operations are. : In fact, the Eiffel seems :completely irrelevant. There _is_ a (small) problem with the terminology :here as you are indicating - specifically when there are mixtures of :specific type and classwide type formals in an operation's signature. I think it's important to recognise that the nature of an operation is different according to the perspective with which it is viewed. If viewed from the standpoint of an actual parameter supplied for a dispatching formal operand, it is realised (for Eiffel, at least) that an implementation specifically tailored for it will be invoked. If viewed from the standpoint of an actual parameter supplied for a classwide formal operand, it is realised that the implementation invoked will handle it generically (using the common characteristics of the family of classes specified by the formal). This distinction is vitally important in the design of singly dispatched software. :However, it does seem perfectly clear to say O is a classwide operation :when only classwide formals are given. For example, : :procedure O1 (X: A'Class); : :function O2 (X: A'Class; Y: B'Class) return C'Class; Agree. : :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com : /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-07 0:00 ` Don Harrison 1996-05-07 0:00 ` Jon S Anthony @ 1996-05-08 0:00 ` Norman H. Cohen 1996-05-08 0:00 ` Robert A Duff 1 sibling, 1 reply; 218+ messages in thread From: Norman H. Cohen @ 1996-05-08 0:00 UTC (permalink / raw) In article <Dr0rp1.1Ks@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: |> Norman H. Cohen lectures Joachim (but in doing so provides a good summary of |> classwide operations): I didn't intend for it to come off as "lecturing Joachim", and I'm sorry if it came off that way. I was just trying to clear up a misconception that many people may well have shared. |> As the equivalent Eiffel shows, the term 'classwide operation' is misleading. |> An operation may be dispatching WRT one parameter but classwide WRT another. |> It is more accurate to speak of classwide operands rather than classwide |> operations. This is a good observation. In the vast majority of naturally occurring examples, the tagged formal parameters tend to be all classwide or all specific (specific = not classwide) and we can get away with informally referring to classwide operations, but in cases like the artificial examples I discussed you are quite right. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-08 0:00 ` Norman H. Cohen @ 1996-05-08 0:00 ` Robert A Duff 1996-05-10 0:00 ` Matt Kennel 0 siblings, 1 reply; 218+ messages in thread From: Robert A Duff @ 1996-05-08 0:00 UTC (permalink / raw) In article <4mqbls$vtc@watnews1.watson.ibm.com>, Norman H. Cohen <ncohen@watson.ibm.com> wrote: >This is a good observation. In the vast majority of naturally occurring >examples, the tagged formal parameters tend to be all classwide or all >specific (specific = not classwide) and we can get away with informally >referring to classwide operations, but in cases like the artificial >examples I discussed you are quite right. I don't see such a big problem. An operation is class-wide if it is class-wide in at least one parameter or result. This means that an operation can be both class-wide and dispatching (e.g. class-wide in one operand, dispatching on the other). After all, if P(Integer, Some_Tagged_Type) is dispatching on Some_Tagged_Type, we don't mind calling it a dispatching operation, even though it is *not* dispatching on Integer. Clearly, "class-wide operation" is a somewhat informal term (whereas "class-wide type" is quite specific and defined in the Ada Reference Manual). I would call a subprogram a class-wide operation if it has a parameter of an access type that points to a class-wide type, for example -- there's an operation that has no class-wide parameters. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-08 0:00 ` Robert A Duff @ 1996-05-10 0:00 ` Matt Kennel 1996-05-10 0:00 ` Robert A Duff 0 siblings, 1 reply; 218+ messages in thread From: Matt Kennel @ 1996-05-10 0:00 UTC (permalink / raw) Robert A Duff (bobduff@world.std.com) wrote: : I don't see such a big problem. An operation is class-wide if it is : class-wide in at least one parameter or result. This means that an : operation can be both class-wide and dispatching (e.g. class-wide in one : operand, dispatching on the other). : After all, if P(Integer, Some_Tagged_Type) is dispatching on : Some_Tagged_Type, we don't mind calling it a dispatching operation, even : though it is *not* dispatching on Integer. : Clearly, "class-wide operation" is a somewhat informal term (whereas : "class-wide type" is quite specific and defined in the Ada Reference : Manual). I would call a subprogram a class-wide operation if it has a : parameter of an access type that points to a class-wide type, for : example -- there's an operation that has no class-wide parameters. I know this is flame bait, but in my opinion, if you're going to have a single 'dispatching'/'controlling'/'tagged' operand you might as well bite the bullet and specifically designated it like Eiffel does. Op(a,b,tagged c) <-> c.Op(a,b) Writing things as Op(a,b,c,d,...) does seem to imply multimethods to most people. (It did to me.) After peeling away the surface Ada has more similar semantics to Eiffel than admitted---yes there really is such a thing as a 'class(Eiffel-usage) interface' of a certain type, even though "theoretically" interfaces are just properties of packages which don't have anything to do with types. : - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-10 0:00 ` Matt Kennel @ 1996-05-10 0:00 ` Robert A Duff 1996-05-14 0:00 ` Matt Kennel 0 siblings, 1 reply; 218+ messages in thread From: Robert A Duff @ 1996-05-10 0:00 UTC (permalink / raw) In article <4n066d$6gl@gaia.ns.utk.edu>, Matt Kennel <kennel@msr.epm.ornl.gov> wrote: >I know this is flame bait, Flame bait? Nah, just a reasonable technical opinion. I hope this doesn't look like a "flame"? >... but in my opinion, if you're going to have >a single 'dispatching'/'controlling'/'tagged' operand you might as well >bite the bullet and specifically designated it like Eiffel does. > > Op(a,b,tagged c) <-> c.Op(a,b) OK, but Ada allows more than one of them to control the dispatching. It's not multimethods -- they all have to have the same tag. But, to me, Union(This_Set, That_Set) ought to make it look like This_Set and That_Set have equal rights -- i.e. it ought to look symmetric. This_Set.Union(That_Set) seems inelegant to me. I wouldn't mind a syntax that made This_Set and That_Set look "special" in some way, but I object to the idea that I have to choose only one of them as the special one. Also, what do you make of Ada's dispatching-on-result? E.g., Union(This_Set, Empty_Set), where Empty_Set is a function, and this dispatches to the "right" Empty_Set based on the tag of This_Set? Maybe that's a silly example, since we know what union with the empty set means. Maybe Union(This_Set, Singleton(37)) is a better example, where Singleton takes an Integer (not dispatching on that Integer), and returns a set. Or maybe, Intersection(Union(Singleton(37), Singleton(24)), This_Set) where the tag of This_Set controls which Singleton operation to dispatch to. What about operations that belong with the type, but don't depend on any particular object of the type? In Smalltalk, it would be a class method. Ada's packages are like Smalltalk's class objects (although rather less dynamic). >Writing things as Op(a,b,c,d,...) does seem to imply multimethods to >most people. (It did to me.) Perhaps. I don't think the Ada Reference Manual gives that impression. >After peeling away the surface Ada has more similar semantics to >Eiffel than admitted---yes there really is >such a thing as a 'class(Eiffel-usage) interface' of a certain >type, even though "theoretically" interfaces are just properties of >packages which don't have anything to do with types. Admitted? Yes, Ada and Eiffel are quite similar, in many ways. I will certainly admit that. There's a notational difference, in that the dispatching operands aren't syntactically distinguished in Ada. There are (fairly minor) functionality differences. And Ada doesn't need "system validity checks". One fairly important difference is the distinction that Ada makes clear between a particular class/type in the hierarchy, vs. all class/types descended from there -- other languages, including Eiffel and C++ obscure that distinction. There are other differences, too, but I agree that there's a lot of similarity (both are strongly typed (at compile time) OOP languages). - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-10 0:00 ` Robert A Duff @ 1996-05-14 0:00 ` Matt Kennel 1996-05-15 0:00 ` Robert A Duff 0 siblings, 1 reply; 218+ messages in thread From: Matt Kennel @ 1996-05-14 0:00 UTC (permalink / raw) Robert A Duff (bobduff@world.std.com) wrote: : In article <4n066d$6gl@gaia.ns.utk.edu>, : Matt Kennel <kennel@msr.epm.ornl.gov> wrote: : >I know this is flame bait, : Flame bait? Nah, just a reasonable technical opinion. I hope this : doesn't look like a "flame"? : >... but in my opinion, if you're going to have : >a single 'dispatching'/'controlling'/'tagged' operand you might as well : >bite the bullet and specifically designated it like Eiffel does. : > : > Op(a,b,tagged c) <-> c.Op(a,b) : OK, but Ada allows more than one of them to control the dispatching. : It's not multimethods -- they all have to have the same tag. But, to : me, Union(This_Set, That_Set) ought to make it look like This_Set and : That_Set have equal rights -- i.e. it ought to look symmetric. : This_Set.Union(That_Set) seems inelegant to me. This is a special case, because in general a compiler wouldn't know that "Union(X,Y)" is the same as "Union(Y,X)"---for all it cares, you can spell "Union" as "MagicFrobozzSubroutine". Personally, I advocate an alternate syntax for method calls single dispatching languages: '(x routine y)' is an alternate way of saying 'x.routine(y)' `A spoonfull of sugar makes the medicine go down' : Also, what do you make of Ada's dispatching-on-result? E.g., : Union(This_Set, Empty_Set), where Empty_Set is a function, and this : dispatches to the "right" Empty_Set based on the tag of This_Set? Maybe : that's a silly example, since we know what union with the empty set : means. Maybe Union(This_Set, Singleton(37)) is a better example, where : Singleton takes an Integer (not dispatching on that Integer), and : returns a set. Or maybe, : Intersection(Union(Singleton(37), Singleton(24)), This_Set) : where the tag of This_Set controls which Singleton operation to dispatch : to. Whoa! Neat. I think...!? And how is it supposed to know that Singleton has anything to do with Sets and has a 'dispatch axis' along the type of This_Set? Suppose the "This_Set" variable was type SET_A, but you wanted to create a variable of type SET_B from the Union? Is it only me but does this feel a bit too much like "spooky action at a distance"? :-) : What about operations that belong with the type, but don't depend on any : particular object of the type? In Smalltalk, it would be a class : method. Ada's packages are like Smalltalk's class objects (although : rather less dynamic). You can put those into classes just fine. After all, these are what constructors are anyway. I prefer that Eiffel & friends really ought to have been called "class-oriented" langauges, you cannot attach particular methods to particular *objects* of course! You can denote methods for classes, and particular objects can be members of certain classes. : >Writing things as Op(a,b,c,d,...) does seem to imply multimethods to : >most people. (It did to me.) : Perhaps. I don't think the Ada Reference Manual gives that impression. : >After peeling away the surface Ada has more similar semantics to : >Eiffel than admitted---yes there really is : >such a thing as a 'class(Eiffel-usage) interface' of a certain : >type, even though "theoretically" interfaces are just properties of : >packages which don't have anything to do with types. : Admitted? Yes, Ada and Eiffel are quite similar, in many ways. I will : certainly admit that. There's a notational difference, in that the : dispatching operands aren't syntactically distinguished in Ada. The difference that I was pointing out is that Ada does indeed have the notion of a 'type interface' (mumble something about primitive operations of a blah blah blah), but it is implicit, whereas Eiffel makes it plain as day. In Ada you joyfully write 'packages' and 'package interfaces' but you still have to end up thinking about types and type interfaces too. I feel Eiffel is 'more honest' in this respect. : There : are (fairly minor) functionality differences. And Ada doesn't need : "system validity checks". One fairly important difference is the : distinction that Ada makes clear between a particular class/type in the : hierarchy, vs. all class/types descended from there -- other languages, : including Eiffel and C++ obscure that distinction. This is an intentional decision of Dr Meyer. C++? It sort of came out in the wash. I prefer Sather which more explicitly distinguishes the two, like Ada. : - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-14 0:00 ` Matt Kennel @ 1996-05-15 0:00 ` Robert A Duff 0 siblings, 0 replies; 218+ messages in thread From: Robert A Duff @ 1996-05-15 0:00 UTC (permalink / raw) In article <4nadoc$pa7@gaia.ns.utk.edu>, Matt Kennel <kennel@msr.epm.ornl.gov> wrote: >This is a special case, because in general a compiler wouldn't know >that "Union(X,Y)" is the same as "Union(Y,X)"---for all it cares, >you can spell "Union" as "MagicFrobozzSubroutine". I don't demand that the compiler know Union is commutative. I just ask that I can declare a Union that treats its arguments symmetrically, with respect to visibility and syntax. "-" isn't commutative, but X - Y, to me, looks like X and Y have equal rights -- the idea that X knows how to subtract Y from itself seems silly to me. >Personally, I advocate an alternate syntax for method >calls single dispatching languages: > > '(x routine y)' is an alternate way of saying 'x.routine(y)' What if "routine" has three args? >`A spoonfull of sugar makes the medicine go down' Indeed. And a spoonful of vinegar? ;-) >: Intersection(Union(Singleton(37), Singleton(24)), This_Set) > >: where the tag of This_Set controls which Singleton operation to dispatch >: to. > >Whoa! Neat. I think...!? ;-) This is not a major feature, but I think it *is* kind of neat. >And how is it supposed to know that Singleton has anything to do >with Sets and has a 'dispatch axis' along the type of This_Set? Singleton is dispatching-on-result if and only if it is declared in the same package as Set, and has a return type of Set. >Suppose the "This_Set" variable was type SET_A, but you wanted to >create a variable of type SET_B from the Union? Then you would use a class-wide operation, instead of a dispatching operation. >Is it only me but does this feel a bit too much like "spooky action >at a distance"? :-) Distance? It's only within a single statement, so it's not so horrible. >: What about operations that belong with the type, but don't depend on any >: particular object of the type? In Smalltalk, it would be a class >: method. Ada's packages are like Smalltalk's class objects (although >: rather less dynamic). > >You can put those into classes just fine. After all, these are what >constructors are anyway. True. It seems kludgy to me, in C++, that constructors are part of the same class. Smalltalk is more elegant, in that it has a separate class object, and the constructors belong to that. Ada is similar, in that constructors are part of the package, not part of every object of the type. Smalltalk class objects are like Ada packages, in this sense, although Smalltalk class objects are much more dynamic, and therefore more powerful. >... I prefer that Eiffel & friends really ought to >have been called "class-oriented" langauges, Agree. >you cannot attach particular methods to particular *objects* of course! >You can denote methods for classes, and particular objects can be members of >certain classes. But the syntax "X.Foo(Y)" or "x foo: y" makes it look like Foo is contained within X, which is wrong, since all X's share the *same* Foo for the whole class. >The difference that I was pointing out is that Ada does indeed have >the notion of a 'type interface' (mumble something about primitive operations >of a blah blah blah), but it is implicit, whereas Eiffel makes it plain as day. Eiffel makes *something* plain as day. Namely, the interface of all ops that take that class as the *first* argument. There's nothing in Eiffel that makes "plain as day" the fact that various random operations take class C as the *third* argument. >In Ada you joyfully write 'packages' and 'package interfaces' but you still >have to end up thinking about types and type interfaces too. True. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Ada terminology (was Re: Real OO) 1996-05-06 0:00 ` Norman H. Cohen 1996-05-07 0:00 ` Don Harrison @ 1996-05-07 0:00 ` David Hopwood 1996-05-07 0:00 ` The Right Reverend Colin James III ` (2 more replies) 1 sibling, 3 replies; 218+ messages in thread From: David Hopwood @ 1996-05-07 0:00 UTC (permalink / raw) In article <4mls4h$sau@watnews1.watson.ibm.com>, Norman H. Cohen <ncohen@watson.ibm.com> wrote: > >In Ada, a class is not a type, but a SET OF TYPES. In particular, the >set of tagged types directly or indirectly derived from a common root >forms a "derivation class". ("Deriving" from a type means defining a >descendant type.) For each type T at the root of a tree or >subtree in the derivation hierarchy, there is a "classwide type", >T'Class, whose values are taken from the discriminated union of the types >in the class. One cannot derive from a classwide type. >(By "discriminated union," we mean that for each value V of each type in >the class, there is a corresponding value in T'Class, and that the >corresponding value in T'Class includes a "tag" identifying the type of >the original value V.) Maybe it's just me, but... Why does Ada use the words 'type' and 'class' in the opposite sense to everyone else? David Hopwood david.hopwood@lmh.ox.ac.uk ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Ada terminology (was Re: Real OO) 1996-05-07 0:00 ` Ada terminology (was Re: Real OO) David Hopwood @ 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-07 0:00 ` Dave Jones 1996-05-07 0:00 ` Tucker Taft 2 siblings, 0 replies; 218+ messages in thread From: The Right Reverend Colin James III @ 1996-05-07 0:00 UTC (permalink / raw) Remove comp.lang.eiffel from distribution of this thread. ------------------------------------------------------------- lady0065@sable.ox.ac.uk (David Hopwood) posted with deletions: | In article <4mls4h$sau@watnews1.watson.ibm.com>, | Norman H. Cohen <ncohen@watson.ibm.com> wrote: | > | >In Ada, a class is not a type, but a SET OF TYPES. In particular, the | >set of tagged types directly or indirectly derived from a common root | >forms a "derivation class". ("Deriving" from a type means defining a | >descendant type.) For each type T at the root of a tree or | >subtree in the derivation hierarchy, there is a "classwide type", | >T'Class, whose values are taken from the discriminated union of the types | >in the class. One cannot derive from a classwide type. | >(By "discriminated union," we mean that for each value V of each type in | >the class, there is a corresponding value in T'Class, and that the | >corresponding value in T'Class includes a "tag" identifying the type of | >the original value V.) | | Maybe it's just me, but... | Why does Ada use the words 'type' and 'class' in the opposite sense | to everyone else? | | David Hopwood | david.hopwood@lmh.ox.ac.uk ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Re-usable, patented software for banks and financial markets. Mirror sites: www.cec-services.com & www.cris.com/~cjames3 Colin James III, Principal Scientist, cjames@cec-services.com CEC Services, LLC, 2080 Kipling St, Lakewood, CO 80215-1502 Voice: 303.231.9437; Facs: 303.231.9438; Data: 303.231.9434 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Ada terminology (was Re: Real OO) 1996-05-07 0:00 ` Ada terminology (was Re: Real OO) David Hopwood 1996-05-07 0:00 ` The Right Reverend Colin James III @ 1996-05-07 0:00 ` Dave Jones 1996-05-07 0:00 ` Tucker Taft 2 siblings, 0 replies; 218+ messages in thread From: Dave Jones @ 1996-05-07 0:00 UTC (permalink / raw) Cc: davedave David Hopwood wrote: > > Maybe it's just me, but... > Why does Ada use the words 'type' and 'class' in the opposite sense > to everyone else? > This has always driven me crazy too. The reasons for Ada using a different set of terms are purely historical. (Basically, it has to do with the fact that Ada was designed in the 70's before OO became firmly established.) There is no functional reason for the differences in terminology. If someone would post an authoritative Smalltalk <--> Ada or C++ <--> Ada conversion chart, that would be nice. -- Dave Jones davedave@io.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Ada terminology (was Re: Real OO) 1996-05-07 0:00 ` Ada terminology (was Re: Real OO) David Hopwood 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-07 0:00 ` Dave Jones @ 1996-05-07 0:00 ` Tucker Taft 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-08 0:00 ` bill.williams 2 siblings, 2 replies; 218+ messages in thread From: Tucker Taft @ 1996-05-07 0:00 UTC (permalink / raw) David Hopwood (lady0065@sable.ox.ac.uk) wrote: : Maybe it's just me, but... : Why does Ada use the words 'type' and 'class' in the opposite sense : to everyone else? Various reasons: 1) History -- Ada has used the terms "type" and "class of types" since 1979. 2) "Class" is a synonym for "set" in mathematics, so at least we use "class" the same way mathematicians do. 3) In many OOPs, the term "class" means 3 different things, depending on context: a) A syntactic construct that encapsulates the data and function component definitions; b) A particular type defined by a "class" construct; c) The set of types containing a type and all its descendants. In Ada, these three concepts have three different names: a) A package b) A type c) A (derivation) class (of types) Your mileage may vary... : David Hopwood : david.hopwood@lmh.ox.ac.uk -Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ Intermetrics, Inc. Cambridge, MA USA ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Ada terminology (was Re: Real OO) 1996-05-07 0:00 ` Tucker Taft @ 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-08 0:00 ` bill.williams 1 sibling, 0 replies; 218+ messages in thread From: The Right Reverend Colin James III @ 1996-05-07 0:00 UTC (permalink / raw) Stop posting this crap to comp.lang.eiffel. ------------------------------------------------------------- stt@henning.camb.inmet.com (Tucker Taft) posted with deletions: | David Hopwood (lady0065@sable.ox.ac.uk) wrote: | | : Maybe it's just me, but... | : Why does Ada use the words 'type' and 'class' in the opposite sense | : to everyone else? | | Various reasons: | | 1) History -- Ada has used the terms "type" and "class of types" | since 1979. | | 2) "Class" is a synonym for "set" in mathematics, so at least | we use "class" the same way mathematicians do. | | 3) In many OOPs, the term "class" means 3 different things, depending | on context: | a) A syntactic construct that encapsulates the data and | function component definitions; | b) A particular type defined by a "class" construct; | c) The set of types containing a type and all its descendants. | | In Ada, these three concepts have three different names: | | a) A package | b) A type | c) A (derivation) class (of types) | | Your mileage may vary... | | : David Hopwood | : david.hopwood@lmh.ox.ac.uk | | -Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ | Intermetrics, Inc. Cambridge, MA USA ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Re-usable, patented software for banks and financial markets. Mirror sites: www.cec-services.com & www.cris.com/~cjames3 Colin James III, Principal Scientist, cjames@cec-services.com CEC Services, LLC, 2080 Kipling St, Lakewood, CO 80215-1502 Voice: 303.231.9437; Facs: 303.231.9438; Data: 303.231.9434 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Ada terminology (was Re: Real OO) 1996-05-07 0:00 ` Tucker Taft 1996-05-07 0:00 ` The Right Reverend Colin James III @ 1996-05-08 0:00 ` bill.williams 1 sibling, 0 replies; 218+ messages in thread From: bill.williams @ 1996-05-08 0:00 UTC (permalink / raw) Don't really want to nitpick, but I'm sorry, I couldn't resist... stt@henning.camb.inmet.com (Tucker Taft) writes: > David Hopwood (lady0065@sable.ox.ac.uk) wrote: > > : Maybe it's just me, but... > : Why does Ada use the words 'type' and 'class' in the opposite sense > : to everyone else? > [...] > > 2) "Class" is a synonym for "set" in mathematics, so at least > we use "class" the same way mathematicians do. In other OOP languages, class is used to characterise a "set" of objects so we do too. > Your mileage may vary... Yes :-) > : David Hopwood > : david.hopwood@lmh.ox.ac.uk > > -Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ > Intermetrics, Inc. Cambridge, MA USA > Bill Williams -- Bill Williams |GEC-Marconi does not necessarily GEC-Marconi Research Centre |endorse my opinions! bill.williams@gecm.com Tel: +44 1245 242016 Fax: +44 1245 242003 ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-02 0:00 ` Joachim Durchholz 1996-05-05 0:00 ` Robert A Duff 1996-05-06 0:00 ` Norman H. Cohen @ 1996-05-07 0:00 ` Amit Patel 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-08 0:00 ` Don Harrison 2 siblings, 2 replies; 218+ messages in thread From: Amit Patel @ 1996-05-07 0:00 UTC (permalink / raw) Joachim Durchholz <jhd@herold.franken.de> wrote: >2) Combinatorial explosion. > >The number of possible class-wide functions is (number of >descendants of A) times (number of descendants of B) >("descendant" including the class itself here). > >This explosion may not be visible in the source code; however, >this appearance is deceiving. After all, even if there is no code >necessary for most of the combinations, you have to check *all* >of the combinations and look wether an overriding class-wide >function is necessary. > >Even worse, classes cannot be viewd as independent entities >anymore. Whenever a new class is added, all combinations with all >classes for wich a multiply dispatching routine is defined have >to be reviewed. [Sarcasm (sorry) on] I consider this a good >scenario for a horror story... but not for software development. (about the loss of independence of classes/modules) Agreed. >Conclusion > >I'm not expert enough to actually determine wether the available >design patterns will work in every case. > >However, the combinatorial explosion effect makes me more than >reluctant to accept multiple dispatching as a desirable feature, >even if it is useful in special cases. And I suspect any solution >that controls this explosion also opens an avenue to designing a >single-inheritance class hierarchy. The combinatorial explosion is for *potential* definitions, but in most designs, it won't actually occur. You'll either write lots of new code for existing classes (reuse of classes) or write lots of new classes for existing code (reuse of code). (In the dictionary's lookup matrix, you'll get columns or rows, but not a dense matrix.) The problem is that you can't assume it will *never* occur. In cases that it does, multiple dispatch is great! :) If the problem requires A x B different pieces of code, then you're going to have to put A x B pieces of code in your program, whether you have multiple dispatch or not. Having multiple dispatch as a solution technique makes it much (?) easier to write your program when the problem you're solving requires it. I'm much more comfortable with languages that don't force me to use a multiple dispatch paradigm, for the reasons you've stated, but I'm convinced that the design patterns *don't* work in all cases, and multiple dispatches really is needed in some cases. Having this feature as an option would be nice. (I'd only use it when the matrix is fairly dense.) -- Amit ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-07 0:00 ` Real OO Amit Patel @ 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-08 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: The Right Reverend Colin James III @ 1996-05-07 0:00 UTC (permalink / raw) Stop posting this crap to comp.lang.eiffel -------------------------------------------------------------- amitp@Xenon.Stanford.EDU (Amit Patel) posted with deletions: | Joachim Durchholz <jhd@herold.franken.de> wrote: | >2) Combinatorial explosion. | > | >The number of possible class-wide functions is (number of | >descendants of A) times (number of descendants of B) | >("descendant" including the class itself here). | > | >This explosion may not be visible in the source code; however, | >this appearance is deceiving. After all, even if there is no code | >necessary for most of the combinations, you have to check *all* | >of the combinations and look wether an overriding class-wide | >function is necessary. | > | >Even worse, classes cannot be viewd as independent entities | >anymore. Whenever a new class is added, all combinations with all | >classes for wich a multiply dispatching routine is defined have | >to be reviewed. [Sarcasm (sorry) on] I consider this a good | >scenario for a horror story... but not for software development. | | (about the loss of independence of classes/modules) Agreed. | | >Conclusion | > | >I'm not expert enough to actually determine wether the available | >design patterns will work in every case. | > | >However, the combinatorial explosion effect makes me more than | >reluctant to accept multiple dispatching as a desirable feature, | >even if it is useful in special cases. And I suspect any solution | >that controls this explosion also opens an avenue to designing a | >single-inheritance class hierarchy. | | The combinatorial explosion is for *potential* definitions, but in | most designs, it won't actually occur. You'll either write lots of | new code for existing classes (reuse of classes) or write lots of new | classes for existing code (reuse of code). (In the dictionary's | lookup matrix, you'll get columns or rows, but not a dense matrix.) | | The problem is that you can't assume it will *never* occur. In cases | that it does, multiple dispatch is great! :) If the problem requires | A x B different pieces of code, then you're going to have to put A x B | pieces of code in your program, whether you have multiple dispatch or | not. Having multiple dispatch as a solution technique makes it much | (?) easier to write your program when the problem you're solving | requires it. | | I'm much more comfortable with languages that don't force me to use a | multiple dispatch paradigm, for the reasons you've stated, but I'm | convinced that the design patterns *don't* work in all cases, and | multiple dispatches really is needed in some cases. Having this | feature as an option would be nice. (I'd only use it when the matrix | is fairly dense.) | | | | -- Amit ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Re-usable, patented software for banks and financial markets. Mirror sites: www.cec-services.com & www.cris.com/~cjames3 Colin James III, Principal Scientist, cjames@cec-services.com CEC Services, LLC, 2080 Kipling St, Lakewood, CO 80215-1502 Voice: 303.231.9437; Facs: 303.231.9438; Data: 303.231.9434 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-07 0:00 ` Real OO Amit Patel 1996-05-07 0:00 ` The Right Reverend Colin James III @ 1996-05-08 0:00 ` Don Harrison 1996-05-08 0:00 ` Juergen Schlegelmilch 1 sibling, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-05-08 0:00 UTC (permalink / raw) Amit Patel writes: : Joachim Durchholz <jhd@herold.franken.de> wrote: :>2) Combinatorial explosion. [...] :>However, the combinatorial explosion effect makes me more than :>reluctant to accept multiple dispatching as a desirable feature, :>even if it is useful in special cases. And I suspect any solution :>that controls this explosion also opens an avenue to designing a :>single-inheritance class hierarchy. This would be true if it were mandatory to provide an implementation for every possible parameter combination. Of course, if that were so, you would hope developers would control it voluntarily by avoiding it. Pity the person who didn't. :-( This would be done by freezing parameters. For example, instead of f (a: A; b: B; c: C) -- triple dispatched treat c generically: f (a: A; b: B; c: frozen C) -- double dispatched or, treating b and c generically; f (a: A; b: frozen B; c: frozen C) -- single dispatched. A mathematical analogy might be that of partial differentiation. BTW, this suggests that the defaults should be reversed. That is, parameters should be frozen (classwide) by default and dispatching parameters should be explicitly declared as such. The above might become: f (a: *A; b: *B; c: *C) -- triple dispatched f (a: *A; b: *B; c: C) -- double dispatched f (a: *A; b: B; c: C) -- single dispatched. That should confuse the C/C++ fraternity. :-) :The combinatorial explosion is for *potential* definitions, but in :most designs, it won't actually occur. You'll either write lots of :new code for existing classes (reuse of classes) or write lots of new :classes for existing code (reuse of code). (In the dictionary's :lookup matrix, you'll get columns or rows, but not a dense matrix.) : :The problem is that you can't assume it will *never* occur. In cases :that it does, multiple dispatch is great! :) If the problem requires :A x B different pieces of code, then you're going to have to put A x B :pieces of code in your program, whether you have multiple dispatch or :not. Having multiple dispatch as a solution technique makes it much :(?) easier to write your program when the problem you're solving :requires it. Agree. Simulating it using single dispatching would be awkward. :I'm much more comfortable with languages that don't force me to use a :multiple dispatch paradigm, for the reasons you've stated, but I'm :convinced that the design patterns *don't* work in all cases, and :multiple dispatches really is needed in some cases. Having this :feature as an option would be nice. (I'd only use it when the matrix :is fairly dense.) One way of controlling both the ambiguity and combinatorial explosion problems would be to only require that an implementation be provided if actually used. That is, if a call is made with a specific combination of actual parameters. If this can be determined statically (difficult), then it could be required as a static constraint for a statically typed language. If not, then it could be imposed as a dynamic constraint for both statically and dynamically typed languages. In this case, an exception would be raised if the specific implementation corresponding to a parameter combination is not found. I think I prefer the dynamic approach. In the context of Ziggy, failure to dispatch as a result of broken multiple dispatching would simply be another manifestation of broken polymorphism. System level validity checking would be a runtime phenomenon. : : -- Amit /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Don Harrison donh@syd.csa.com.au ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-08 0:00 ` Don Harrison @ 1996-05-08 0:00 ` Juergen Schlegelmilch [not found] ` <Dr4538.D27@assip.csasyd.oz> 1996-05-20 0:00 ` Joachim Durchholz 0 siblings, 2 replies; 218+ messages in thread From: Juergen Schlegelmilch @ 1996-05-08 0:00 UTC (permalink / raw) On Wed, 8 May 1996 07:49:05 GMT, Don Harrison <donh@syd.csa.com.au> wrote: > > One way of controlling both the ambiguity and combinatorial explosion problems > would be to only require that an implementation be provided if actually used. > That is, if a call is made with a specific combination of actual parameters. > > If this can be determined statically (difficult), then it could be required as > a static constraint for a statically typed language. If not, then it could be > imposed as a dynamic constraint for both statically and dynamically typed > languages. In this case, an exception would be raised if the specific > implementation corresponding to a parameter combination is not found. There is a technical report from Craig Chambers and Gary Leavens (Iowa State University, Aug 1995) about type checking multi-dispatched methods statically (in the Cecil language). The algorithm presented in the paper is not exponential, and finds all possible conflicts at compile time. I think this is what you are searching for. It will allow you to supply only the implementations you found necessary, run the type checker and add further implementations as required for safe dispatch; the additional implementations are either really necessary (i.e. the combination of classes can occur in your program) and you simply forgot them, or they are not needed because you know (how?) that the associated combination of classes will not occur, so you have to give the type checker more information. Note that this latter approach is not covered in the paper. I don't remember whether the algorithm suggests a minimal number of additional implementations, or just a set of implementations that will resolve all conflicts. > I think I prefer the dynamic approach. In the context of Ziggy, failure to > dispatch as a result of broken multiple dispatching would simply be another > manifestation of broken polymorphism. System level validity checking would > be a runtime phenomenon. Yes, dispatch failure for multiple dispatch indicates broken polymorphism. The design of inheritance hierarchies is often not seen in the light of the Liskov Substitution Principle, but rather under the aspect of code reuse. This may lead to hierarchies in the sense of taxonomies (i.e. specialisation by restriction) which make polymorphism a difficult thing. Multi-dispatch adds to these difficulties by breaking up the object/class- centered view: you have to consider combinations instead of single objects. I hope this helps a bit. Juergen -- +-----------------------------------------------------------------------------+ Dipl.-Inf. Juergen Schlegelmilch University of Rostock email: schlegel@Informatik.Uni-Rostock.de Computer Science Department http://www.informatik.uni-rostock.de/~schlegel Database Research Group Tel: ++49 381 498 3402 18051 Rostock Fax: ++49 381 498 3404 Germany +-----------------------------------------------------------------------------+ ^ permalink raw reply [flat|nested] 218+ messages in thread
[parent not found: <Dr4538.D27@assip.csasyd.oz>]
* Re: Real OO [not found] ` <Dr4538.D27@assip.csasyd.oz> @ 1996-05-09 0:00 ` Juergen Schlegelmilch 1996-05-09 0:00 ` Richard Riehle 1 sibling, 0 replies; 218+ messages in thread From: Juergen Schlegelmilch @ 1996-05-09 0:00 UTC (permalink / raw) On Thu, 9 May 1996 00:52:19 GMT, Don Harrison <donh@syd.csa.com.au> wrote: > Thanks for the pointer. Have asked cecil@cs.washington.edu for more info. Sorry, I forgot to include numbers in my pointer: The technical report is available as University of Washington CS&E Technical Report 95-08-05 Iowa State University CS Technical Report #95-19 It also says an earlier version appeared in the proceedings of OOPSLA'94. > :the Liskov Substitution Principle, > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > Could you (or somone) explain what this is? Sorry, I'm a bit ignorant. This goes back to an article: Liskov, Barbara. "Data Abstraction and Hierarchy." SIGPLAN Notices 23,5 (May 1988) pg 25. It says that objects of a subclass must not only be structurally substitutable (i.e. show the same interface) but also behaviourally, i.e. they have to accept the same set of event sequences. First of all, this means you cannot have (single-dispatch with) covariance since this allows an object in a subclass to require more specific parameters. Second and more important, the observable changes in the object must be equivalent for all sequences of events (= method calls). --- I have to admid that I never read the original article from Barbara Liskov, so anyone can correct me on the exact definition. Regards, Juergen -- +-----------------------------------------------------------------------------+ Dipl.-Inf. Juergen Schlegelmilch University of Rostock email: schlegel@Informatik.Uni-Rostock.de Computer Science Department http://www.informatik.uni-rostock.de/~schlegel Database Research Group Tel: ++49 381 498 3402 18051 Rostock Fax: ++49 381 498 3404 Germany +-----------------------------------------------------------------------------+ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO [not found] ` <Dr4538.D27@assip.csasyd.oz> 1996-05-09 0:00 ` Juergen Schlegelmilch @ 1996-05-09 0:00 ` Richard Riehle 1996-05-10 0:00 ` Tucker Taft 1996-05-14 0:00 ` James McKim 1 sibling, 2 replies; 218+ messages in thread From: Richard Riehle @ 1996-05-09 0:00 UTC (permalink / raw) To: Don Harrison On Thu, 9 May 1996, Don Harrison wrote: > Juergen Schlegelmilch writes: > > :Yes, dispatch failure for multiple dispatch indicates broken polymorphism. > :The design of inheritance hierarchies is often not seen in the light of > :the Liskov Substitution Principle, > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > Could you (or somone) explain what this is? Sorry, I'm a bit ignorant. You might want to look at a paper by Liskov, "Subtype Hierarchy and Implementation Hierarchy," Proceedings of the ACM Conference on Object-Oriented Programming, 1987 Though the substitutability principle is often attributed to Liskov, it is also given treatment in a paper from Wegner and Zdonic, "Inheritance as an Incremental Modification Mechanism" published in the Proceedings of the European Conference on Object-Oriented Programming, 1988. An important overview of this subject is Cardelli and Wegner, "On Understanding Types, Data Abstraction, and Polymorphism," ACM Computing Surveys, Number 17, 1985. -- ================================================================= All that academic stuff aside, the substitutability rule is quite useful when trying to determine whether one is organizing a set of objects by classification or by some other mechanism. In particular, there is a point-of-view which says one should always apply the substitutability principle when attempting multiple inheritance. Richard Riehle ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-09 0:00 ` Richard Riehle @ 1996-05-10 0:00 ` Tucker Taft 1996-05-13 0:00 ` Don Harrison 1996-05-14 0:00 ` James McKim 1 sibling, 1 reply; 218+ messages in thread From: Tucker Taft @ 1996-05-10 0:00 UTC (permalink / raw) Richard Riehle (rriehle@nunic.nu.edu) wrote: : ... : Though the substitutability principle is often attributed to Liskov, : it is also given treatment in a paper from Wegner and Zdonic, : "Inheritance as an Incremental Modification Mechanism" published in : the Proceedings of the European Conference on : Object-Oriented Programming, 1988. : An important overview of this subject is Cardelli and Wegner, "On : Understanding Types, Data Abstraction, and Polymorphism," ACM : Computing Surveys, Number 17, 1985. : -- ================================================================= : All that academic stuff aside, the substitutability rule is quite : useful when trying to determine whether one is organizing a set of : objects by classification or by some other mechanism. In particular, : there is a point-of-view which says one should always apply the : substitutability principle when attempting multiple inheritance. In Ada 95, the substitutability rule is almost a tautology, if you interpret it as meaning that any place where you can use A'Class, you should be able to substitute an operand of type A1'Class, where A1 is a derivative of A. Since A'Class is a proper superset of A1'Class, it is trivial to show that any procedure that works properly over all operands of "type" A'Class, will also work properly over all operands of "type" A1'Class. What this highlights, in my view, is that the substituability rule only makes sense in terms of an *explicitly* enumerated set of properties or invariants that are presumed over a given set of types. If you then want to add a new type to that set, you must be careful that the new type satisfies all of the properties/invariants presumed by the rest of the system. Clearly it is not possible to satisfy all possible properties imaginable, for example the time or space requirements of two types (and their associated operatios) will almost never be identical. If time/space is critical to the correct operation of the system (e.g. in a real-time embedded system), then you must enumerate the requirements, and verify them for each type in the set. Just saying "substitutability" doesn't really say anything, IMHO. The real issue is establishing a set of "class-wide" requirements, and then verifying them each time a new type is added to the set of types within the class (hierarchy). Eiffel tries to support this effort through class invariants, but I believe it gets tripped up a bit by the conflict between establishing useful invariants for a specific type/class in the class hierarchy, which might want to be quite "tight" so as to catch as many errors as possible, and the appropriate invariants for the entire class hierarchy, which want to be as "loose" as possible to ensure flexibility of extension. There seems to be a need to have two separate kinds of invariants -- those that apply to an individual type/class, and a separate set that get "inherited", and that apply throughout the class hierarchy. : Richard Riehle -Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ Intermetrics, Inc. Cambridge, MA USA ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-10 0:00 ` Tucker Taft @ 1996-05-13 0:00 ` Don Harrison 1996-05-13 0:00 ` Tucker Taft 0 siblings, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-05-13 0:00 UTC (permalink / raw) Tucker Taft writes: :Richard Riehle (rriehle@nunic.nu.edu) wrote: :: :: [references for Liskov substitutability] Thanks for these. [...] :Just saying "substitutability" doesn't really say anything, IMHO. :The real issue is establishing a set of "class-wide" requirements, :and then verifying them each time a new type is added to the set of :types within the class (hierarchy). : :Eiffel tries to support this effort through class invariants, but :I believe it gets tripped up a bit by the conflict between establishing :useful invariants for a specific type/class in the class :hierarchy, which might want to be quite "tight" so as to catch as :many errors as possible, and the appropriate invariants for the :entire class hierarchy, which want to be as "loose" as possible :to ensure flexibility of extension. There seems to be :a need to have two separate kinds of invariants -- those that apply to :an individual type/class, and a separate set that get "inherited", :and that apply throughout the class hierarchy. Because Eiffel invariants are ANDed with ancestor invariants, they become successively tighter in descendants. Doesn't this fit the bill? :: Richard Riehle : :-Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ :Intermetrics, Inc. Cambridge, MA USA /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Don Harrison donh@syd.csa.com.au ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-13 0:00 ` Don Harrison @ 1996-05-13 0:00 ` Tucker Taft 1996-05-14 0:00 ` Joachim Durchholz ` (5 more replies) 0 siblings, 6 replies; 218+ messages in thread From: Tucker Taft @ 1996-05-13 0:00 UTC (permalink / raw) Don Harrison (donh@syd.csa.com.au) wrote: : Tucker Taft writes: : :Eiffel tries to support this effort through class invariants, but : :I believe it gets tripped up a bit by the conflict between establishing : :useful invariants for a specific type/class in the class : :hierarchy, which might want to be quite "tight" so as to catch as : :many errors as possible, and the appropriate invariants for the : :entire class hierarchy, which want to be as "loose" as possible : :to ensure flexibility of extension. There seems to be : :a need to have two separate kinds of invariants -- those that apply to : :an individual type/class, and a separate set that get "inherited", : :and that apply throughout the class hierarchy. : Because Eiffel invariants are ANDed with ancestor invariants, they become : successively tighter in descendants. Doesn't this fit the bill? No. The problem is that when you write an assertion, you must decide whether you are trying to constrain the current type/class as tightly as possible, to catch as many bugs in it as possible, or to constrain it as loosely as possible, to allow as much flexibility as possible in descendant type/classes. Clearly, if the type is deferred/abstract, then the assertions are only of interest in descendant types/classes, and so clearly should be as loose as possible to maximize flexibility of implementation. However, when you have a non-deferred/non-abstract ("concrete") class which nevertheless might have descendants, you end up in the quandary. It would be reasonable to have both kinds of assertions. Right now, I suspect a common scenario is that when someone first writes a "concrete" type/class, they write the assertions relatively tightly. Then, if they later start creating descendants of it, they might discover an assertion that is unnecessarily over constraining, and go back and loosen it on the parent type/class. This loosening may eventually result in overly weak assertions on the parent type/class, so that future maintenance on the parent type/class itself might be more likely to violate what must now be "unwritten" assumptions/assertions. An alternative scenario might be to convert a concrete class into an abstract/deferred one when it starts to run into this problem, with the original concrete functionality being moved down into a descendant, where the tighter assertions may be retained. Another way to look at the problem is whether the assertions are focused on the "client" view or the "server" view. For a deferred/abstract class, there is no real server, so the assertions are clearly client oriented. However for a concrete class, the client view and the server view are different, and it makes sense to keep the client-oriented assertions as weak as possible (but no weaker!), while making a specific server's "local" assertions as strong as possible so as to catch as many bugs as possible. The simplest solution might be to distinguish whether a given assertion should apply only on a specific type/class, or should apply on all descendant types/classes as well. Clients should of course only be interested in those assertions that apply to all descendant classes as well, whenever they want to operate without knowledge of and/or dependence on the specific type/class providing the implementation. : Don. (o o) : =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- : Don Harrison donh@syd.csa.com.au -Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ Intermetrics, Inc. Cambridge, MA USA ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-13 0:00 ` Tucker Taft @ 1996-05-14 0:00 ` Joachim Durchholz 1996-05-14 0:00 ` Roger Browne ` (4 subsequent siblings) 5 siblings, 0 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-05-14 0:00 UTC (permalink / raw) bobduff@world.std.com wrote 14.05.96 on Re: Real OO: > I believe that in Eiffel, a precondition can refer to a private data > item, Yes. > of which the client knows nothing. No, only if all possible clients have access to the private data. Quoting from the language definition: "VAPE: A Precondition of a routine *r* of a class *C* is valid if and only iff every feature whose final name appears in any Assertion_clause is available to every class to which *r* is available." I.e. a precondition must not use a feature that the caller does not know of. If a feature is exported, all features used in the precondition must be exported too. Note that this export may be constrained - a feature may be exported to a few well-known cooperating classes and nowhere else. This means that any features used in the precondition must be exported as well. -Joachim -- Looking for a new job. Resume available on request. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-13 0:00 ` Tucker Taft 1996-05-14 0:00 ` Joachim Durchholz @ 1996-05-14 0:00 ` Roger Browne 1996-05-14 0:00 ` Don Harrison ` (3 subsequent siblings) 5 siblings, 0 replies; 218+ messages in thread From: Roger Browne @ 1996-05-14 0:00 UTC (permalink / raw) Tucker Taft writes: > The simplest solution might be to distinguish whether a given > assertion should apply only on a specific type/class, or should > apply on all descendant types/classes as well... This is trivial in Eiffel. Just include an assertion clause that is a call to a boolean-valued function that can be redefined in descendants. For example: class X feature ... local_invariant: BOOLEAN is do ... end invariant enforced_on_descendants: some_boolean_valued_expression redefinable_in_descendants: local_invariant end Feature 'local_invariant' can be strengthened or weakened in descendants, whereas 'some_boolean_valued_expression' can only be strengthened in descendants (by being implicitly "and"-ed with the invariant of the descendant). Regards, Roger -- -- -- Roger Browne, 6 Bambers Walk, Wesham, PR4 3DG, UK | Ph 01772-687525 -- Everything Eiffel: compilers/libraries/publications | +44-1772-687525 ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-13 0:00 ` Tucker Taft 1996-05-14 0:00 ` Joachim Durchholz 1996-05-14 0:00 ` Roger Browne @ 1996-05-14 0:00 ` Don Harrison 1996-05-14 0:00 ` Robert A Duff ` (3 more replies) 1996-05-15 0:00 ` Alexander Kjeldaas ` (2 subsequent siblings) 5 siblings, 4 replies; 218+ messages in thread From: Don Harrison @ 1996-05-14 0:00 UTC (permalink / raw) Tucker Taft writes: :Don Harrison (donh@syd.csa.com.au) wrote: :: Tucker Taft writes: : :: :Eiffel tries to support this effort through class invariants, but :: :I believe it gets tripped up a bit by the conflict between establishing :: :useful invariants for a specific type/class in the class :: :hierarchy, which might want to be quite "tight" so as to catch as :: :many errors as possible, and the appropriate invariants for the :: :entire class hierarchy, which want to be as "loose" as possible :: :to ensure flexibility of extension. There seems to be :: :a need to have two separate kinds of invariants -- those that apply to :: :an individual type/class, and a separate set that get "inherited", :: :and that apply throughout the class hierarchy. : :: Because Eiffel invariants are ANDed with ancestor invariants, they become :: successively tighter in descendants. Doesn't this fit the bill? : :No. : :The problem is that when you write an assertion, you must :decide whether you are trying to constrain the current type/class :as tightly as possible, to catch as many bugs in it as possible, or :to constrain it as loosely as possible, to allow as much flexibility :as possible in descendant type/classes. Don't think so. I think the objectives of catching bugs and making assertions about correct behaviour are the same. If you invent an assertion to assist in debugging, then it should imply a correctness condition. If you invent a correctness condition, it should also be valuable for debugging. What do others think? IMO, the issue is more one of determining the correct level of abstraction for an assertion. If it is placed at the correct level, then it will apply equally well to the current level and for all descendants. No greater flexibility in descendants should be allowable or needed. If a need for greater flexibility is perceived, then you know the design is incorrect in some way. The solution may be: a) Move the assertion lower down the inheritance hierarchy (If this implies duplicating it in several descendants, then you might consider grouping the common assertional behaviour by interposing an intermediate level of abstraction including the common assertion) b) Splitting it into a common component and a specific component and moving the specific component as in a) :Clearly, if the type is deferred/abstract, then the assertions :are only of interest in descendant types/classes, and so clearly :should be as loose as possible to maximize flexibility of :implementation. However, when you have a non-deferred/non-abstract :("concrete") class which nevertheless might have descendants, you end :up in the quandary. I don't think the generality of assertions for abstract and concrete classes should differ. In both cases, you are prescribing what their common behaviour should be. That common behaviour is expected of any legitimate children of that class. Whether or not that behaviour is actually implemented in the parent shouldn't matter, IMO. :It would be reasonable to have both kinds of assertions. : :Right now, I suspect a common scenario is that when someone first :writes a "concrete" type/class, they write the assertions :relatively tightly. Then, if they later start creating :descendants of it, they might discover an assertion that :is unnecessarily over constraining, and go back and loosen :it on the parent type/class. This loosening may eventually :result in overly weak assertions on the parent type/class, so :that future maintenance on the parent type/class itself might be :more likely to violate what must now be "unwritten" :assumptions/assertions. I think the situation you describe here should be dealt with by my suggestions above. :An alternative scenario might be to convert a concrete class :into an abstract/deferred one when it starts to run into :this problem, with the original concrete functionality being :moved down into a descendant, where the tighter assertions :may be retained. Better, IMO, would be to get the assertions right and leave the implementation where you intended it to be (if it's the right place). :Another way to look at the problem is whether the assertions :are focused on the "client" view or the "server" view. :For a deferred/abstract class, there is no real server, :so the assertions are clearly client oriented. However :for a concrete class, the client view and the server view :are different, and it makes sense to keep the client-oriented :assertions as weak as possible (but no weaker!), while making :a specific server's "local" assertions as strong as possible so :as to catch as many bugs as possible. Someone correct me if wrong, but in the contracting scheme of things, the client would need to be aware of *all* the requisite conditions that make a call legal, irrespective of whether they pertain to the target of the call or another object. In this sense, all preconditions are client-oriented. WRT assertions on abstract classes, they still express design intent that states preconditions that must be known to prevail by potential clients before they venture to call the operation. :The simplest solution might be to distinguish whether a given :assertion should apply only on a specific type/class, or should :apply on all descendant types/classes as well. Clients should of :course only be interested in those assertions that apply to all :descendant classes as well, whenever they want to operate without :knowledge of and/or dependence on the specific type/class :providing the implementation. ... which is always in Eiffel. Unless clients are aware of all conditions that are required for an operation to work properly, they can have no assurance that it will deliver what they expect. :-Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ :Intermetrics, Inc. Cambridge, MA USA /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Don Harrison donh@syd.csa.com.au ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-14 0:00 ` Don Harrison @ 1996-05-14 0:00 ` Robert A Duff 1996-05-14 0:00 ` Steve Tynor ` (2 subsequent siblings) 3 siblings, 0 replies; 218+ messages in thread From: Robert A Duff @ 1996-05-14 0:00 UTC (permalink / raw) In article <DrDus7.Dno@assip.csasyd.oz>, Don Harrison <donh@syd.csa.com.au> wrote: >Someone correct me if wrong, but in the contracting scheme of things, >the client would need to be aware of *all* the requisite conditions that >make a call legal, irrespective of whether they pertain to the target of the >call or another object. In this sense, all preconditions are client-oriented. I believe that in Eiffel, a precondition can refer to a private data item, of which the client knows nothing. - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-14 0:00 ` Don Harrison 1996-05-14 0:00 ` Robert A Duff @ 1996-05-14 0:00 ` Steve Tynor 1996-05-14 0:00 ` Robert A Duff 1996-05-15 0:00 ` Don Harrison 1996-05-15 0:00 ` Steve Tynor 1996-05-16 0:00 ` James McKim 3 siblings, 2 replies; 218+ messages in thread From: Steve Tynor @ 1996-05-14 0:00 UTC (permalink / raw) In article <DrEAyt.Hp3@world.std.com> bobduff@world.std.com (Robert A Duff) writes: | Don Harrison <donh@syd.csa.com.au> wrote: | >Someone correct me if wrong, but in the contracting scheme of things, | >the client would need to be aware of *all* the requisite conditions that | >make a call legal, irrespective of whether they pertain to the target of the | >call or another object. In this sense, all preconditions are client-oriented. | | I believe that in Eiffel, a precondition can refer to a private data | item, of which the client knows nothing. In fact, such a precondition would be a violation of the VAPE validity rule (section 9.8, ETL). Our compiler treats this as only a Warning (since there are several PD libraries that use this construct), though technically it should be an Error. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Eiffel: Accept no substitutes. Steve Tynor Email: Steve.Tynor@atlanta.twr.com Tower Technology WWW: http://www.twr.com/ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-14 0:00 ` Steve Tynor @ 1996-05-14 0:00 ` Robert A Duff 1996-05-15 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Robert A Duff @ 1996-05-14 0:00 UTC (permalink / raw) In article <TYNOR.96May14100156@twratl.atlanta.twr.com>, Steve Tynor <tynor@atlanta.twr.com> wrote: >In fact, such a precondition would be a violation of the VAPE validity >rule (section 9.8, ETL). Our compiler treats this as only a Warning >(since there are several PD libraries that use this construct), though >technically it should be an Error. OK, I just re-read 9.8, and I stand corrected. A post-condition can refer to things unknown to the client, but a pre-condition cannot. There is some discussion in 9.8 about why this difference makes sense, and it talks about how the "interface form" will not include post-conditions that have unknown meaning to the client. However, I find the definition of "Availability of an Assertion Clause" in that section confusing. Is it not the case that pre-conditions are always "available"? - Bob P.S. I find it appalling that your compiler is unable to enforce the rules of the language, because some code disobeys those rules. Is this because there exist some compilers that don't bother to enforce the rules?! Yuck. This is a common problem with C, but I thought Eiffel was above that problem. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-14 0:00 ` Steve Tynor 1996-05-14 0:00 ` Robert A Duff @ 1996-05-15 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-05-15 0:00 UTC (permalink / raw) Steve Tynor writes: :In article <DrEAyt.Hp3@world.std.com> bobduff@world.std.com (Robert A Duff) writes: : :| Don Harrison <donh@syd.csa.com.au> wrote: :| >Someone correct me if wrong, but in the contracting scheme of things, :| >the client would need to be aware of *all* the requisite conditions that :| >make a call legal, irrespective of whether they pertain to the target of the :| >call or another object. In this sense, all preconditions are client-oriented. :| :| I believe that in Eiffel, a precondition can refer to a private data :| item, of which the client knows nothing. : :In fact, such a precondition would be a violation of the VAPE validity :rule (section 9.8, ETL). Our compiler treats this as only a Warning :(since there are several PD libraries that use this construct), though :technically it should be an Error. Thanks. That's what I was thinking of (was too slack to look it up). :=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= :Eiffel: Accept no substitutes. : :Steve Tynor Email: Steve.Tynor@atlanta.twr.com :Tower Technology WWW: http://www.twr.com/ /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Don Harrison donh@syd.csa.com.au ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-14 0:00 ` Don Harrison 1996-05-14 0:00 ` Robert A Duff 1996-05-14 0:00 ` Steve Tynor @ 1996-05-15 0:00 ` Steve Tynor 1996-05-15 0:00 ` Robert A Duff 1996-05-16 0:00 ` James McKim 3 siblings, 1 reply; 218+ messages in thread From: Steve Tynor @ 1996-05-15 0:00 UTC (permalink / raw) In article <DrF5r9.BKv@world.std.com> bobduff@world.std.com (Robert A Duff) writes: | However, I find the definition of "Availability of an Assertion Clause" | in that section confusing. Is it not the case that pre-conditions are | always "available"? "Availablity" relates to the "export status" of each feature. If I write: feature {ANY} f is require a > b do .. end a : INTEGER feature {NONE} b : INTEGER VAPE requires that both `a' and `b' are exported to all possible clients of feature `f'. In this case, `a' is available to all the clients of `f' (since it is exported to the same set of classes: {ANY}), but `b' is not. Therefore the precondition is invalid. It's a pretty intuitive concept: it's not reasonable to base a precondition (which imposes constraints on the client) on features which the client has no way of checking for itself. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= You can always sleep in your car, but you can't take your house for a drive. Steve Tynor Email: Steve.Tynor@atlanta.twr.com Tower Technology WWW: http://www.twr.com/ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-15 0:00 ` Steve Tynor @ 1996-05-15 0:00 ` Robert A Duff 0 siblings, 0 replies; 218+ messages in thread From: Robert A Duff @ 1996-05-15 0:00 UTC (permalink / raw) In article <TYNOR.96May15090527@twratl.atlanta.twr.com>, Steve Tynor <tynor@atlanta.twr.com> wrote: >"Availablity" relates to the "export status" of each feature. If I >write: >[...explanation of VAPE deleted] Makes sense, but it doesn't answer my original question. Near the end of 9.8, *after* the VAPE rule, there is a definition of "Availability of an Assertion Clause" (which is not the same thing as availability of a feature, although it's related). My question was, can a precondition ever be "unavailable"? It seems to me that if it were, it would fail VAPE, and therefore wouldn't be allowed in the first place. Am I confused? - Bob ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-14 0:00 ` Don Harrison ` (2 preceding siblings ...) 1996-05-15 0:00 ` Steve Tynor @ 1996-05-16 0:00 ` James McKim 1996-05-18 0:00 ` Matt Kennel 3 siblings, 1 reply; 218+ messages in thread From: James McKim @ 1996-05-16 0:00 UTC (permalink / raw) In article <DrDus7.Dno@assip.csasyd.oz> donh@syd.csa.com.au writes: >Tucker Taft writes: > >:Don Harrison (donh@syd.csa.com.au) wrote: >:: Tucker Taft writes: >: [..] >: >:The problem is that when you write an assertion, you must >:decide whether you are trying to constrain the current type/class >:as tightly as possible, to catch as many bugs in it as possible, or >:to constrain it as loosely as possible, to allow as much flexibility >:as possible in descendant type/classes. > >Don't think so. I think the objectives of catching bugs and making assertions >about correct behaviour are the same. If you invent an assertion to assist in >debugging, then it should imply a correctness condition. If you invent a >correctness condition, it should also be valuable for debugging. What do >others think? > >IMO, the issue is more one of determining the correct level of abstraction >for an assertion. If it is placed at the correct level, then it will apply >equally well to the current level and for all descendants. No greater >flexibility in descendants should be allowable or needed. I think Don is right in theory, but that Tucker is right in practice. In practice I regularly run into situations in which I want a concrete feature that provides default behavior for descendants, but I don't want the descendants to be totally constrained by that behavior. IOW, I'd like two distinct kinds of assertions. 1. Assertions that bind the current routine and its redefinitions in descendants. 2. Assertions that bind the current routine, but not necessarily its redefinitions in descendants. The latter provide the rigorous specification for the default version of the routine. In Eiffel I try to write the first kind as compilable assertions wherever I can, and use rigorous comments for the second. Of course clients can only depend on the stronger assertions if they're sure no dynamic binding has taken place. > >If a need for greater flexibility is perceived, then you know the design is >incorrect in some way. The solution may be: > >a) Move the assertion lower down the inheritance hierarchy (If this implies > duplicating it in several descendants, then you might consider grouping > the common assertional behaviour by interposing an intermediate level of > abstraction including the common assertion) > >b) Splitting it into a common component and a specific component and moving > the specific component as in a) > There is no one correct design for any sufficiently complex system and in this case, "the perfect is the enemy of the good" (wish I knew who said that). In particular, you cannot redesign any reasonably complex inheritance hierarchy every time you run into this problem because a) You may not have control over all the classes involved. A proprietary library, for example. b) Changing the inheritance hierarchy in such a way may have dramatic and unpleasant repercussions in existing code. c) If you really try for perfection you will end up with separate classes for every _feature_ and _combination_ of features where the problem arises. E.g I need an abstraction where features f and g are loosely specified, one where f is constrained but g is not, one where g is constrained but f is not ... Well, you get the idea. Don't get me wrong, I think we should strive to find useful abstractions that help to relieve the problem. You just don't want to go overboard. [..] > >:It would be reasonable to have both kinds of assertions. As noted above, I agree. Of course this isn't a panacea either, as you still have to predict the appropriate constraints on descendants. There ain't no silver bullet (I know who said that :-)). [..] > >:Another way to look at the problem is whether the assertions >:are focused on the "client" view or the "server" view. >:For a deferred/abstract class, there is no real server, >:so the assertions are clearly client oriented. However >:for a concrete class, the client view and the server view >:are different, and it makes sense to keep the client-oriented >:assertions as weak as possible (but no weaker!), while making >:a specific server's "local" assertions as strong as possible so >:as to catch as many bugs as possible. I agree with the spirit of this, but IMHO you should be thinking at the feature level, not the class level. A deferred class may consist of leventy-leven concrete routines and one deferred routine. I'm not sure the "client/server" distinction holds up here as sometimes the only deferred routines in a class are private ones (I suppose you could say the server is its own client in this case). [..] > > >:-Tucker Taft stt@inmet.com http://www.inmet.com/~stt/ >:Intermetrics, Inc. Cambridge, MA USA > > /// >Don. (o o) >=-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- >Don Harrison donh@syd.csa.com.au > > Hope this helps, -- Jim -- *------------------------------------------------------------------------------* Jim McKim (860)-548-2458 Co-editor of Eiffel Outlook Internet: jcm@hgc.edu Subscribe early and often! ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-16 0:00 ` James McKim @ 1996-05-18 0:00 ` Matt Kennel 1996-05-20 0:00 ` James McKim 0 siblings, 1 reply; 218+ messages in thread From: Matt Kennel @ 1996-05-18 0:00 UTC (permalink / raw) James McKim (jcm@hgc.edu) wrote: : I think Don is right in theory, but that Tucker is right in practice. : In practice I regularly run into situations in which I want : a concrete feature that provides default behavior for descendants, : but I don't want the descendants to be totally constrained by that : behavior. IOW, I'd like two distinct kinds of assertions. Perhaps what you want is to be able to inherit implementation without also inheriting interface, which in Eiffel, encompasses assertion as well as routine signatures. This is yet another reason to make such a design choice, either explicitly in your language, or in your style of class design: keep distinct 'concrete leaf classes' which serve *only* as a locus of shared implementation from 'abstract classes' which serve as specification. : Hope this helps, : -- Jim : -- : *------------------------------------------------------------------------------* : Jim McKim (860)-548-2458 Co-editor of Eiffel Outlook : Internet: jcm@hgc.edu Subscribe early and often! ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-18 0:00 ` Matt Kennel @ 1996-05-20 0:00 ` James McKim 1996-05-22 0:00 ` Matt Kennel 0 siblings, 1 reply; 218+ messages in thread From: James McKim @ 1996-05-20 0:00 UTC (permalink / raw) In article <4nl9fj$35m@gaia.ns.utk.edu> kennel@msr.epm.ornl.gov writes: >James McKim (jcm@hgc.edu) wrote: > >: I think Don is right in theory, but that Tucker is right in practice. >: In practice I regularly run into situations in which I want >: a concrete feature that provides default behavior for descendants, >: but I don't want the descendants to be totally constrained by that >: behavior. IOW, I'd like two distinct kinds of assertions. > >Perhaps what you want is to be able to inherit implementation without also >inheriting interface, which in Eiffel, encompasses assertion as well >as routine signatures. Hmmm... At first I thought these were orthogonal issues, but upon reflection I think you may be right. So class X has a feature f that has type semantics and implementation semantics. If we want class Y to be a subtype of X then any redefinition of f in Y is bound by the original type semantics. Now what happens if we want Z to be a subclass (inherit implementation of) X? I'm assuming that in Sather there is no restriction on how Z may redefine f, is that correct? And if W is intended to be both a subtype and a subclass of X then any redefinition of f must follow its original type semantics, but can implement it any old way. Yes, this is appealing.... > >This is yet another reason to make such a design choice, either explicitly in >your language, or in your style of class design: keep distinct >'concrete leaf classes' which serve *only* as a locus of shared implementation >from 'abstract classes' which serve as specification. Lost you here. Are you talking about the subtype or subclass hierarchy? If subclass, why would the leaves share implementation with their abstract ancestors? I would have expected abstract ancestors to be supertypes. If subtype, why mention the sharing of implementation at all? Good thought, -- Jim [..] -- *------------------------------------------------------------------------------* Jim McKim (860)-548-2458 Co-editor of Eiffel Outlook Internet: jcm@hgc.edu Subscribe early and often! ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-20 0:00 ` James McKim @ 1996-05-22 0:00 ` Matt Kennel 0 siblings, 0 replies; 218+ messages in thread From: Matt Kennel @ 1996-05-22 0:00 UTC (permalink / raw) James McKim (jcm@hgc.edu) wrote: : In article <4nl9fj$35m@gaia.ns.utk.edu> kennel@msr.epm.ornl.gov writes: : >Perhaps what you want is to be able to inherit implementation without also : >inheriting interface, which in Eiffel, encompasses assertion as well : >as routine signatures. : Hmmm... At first I thought these were orthogonal issues, but upon reflection : I think you may be right. So class X has a feature f that has type : semantics and implementation semantics. If we want class Y to be a subtype : of X then any redefinition of f in Y is bound by the original type : semantics. In Sather, if concrete class Y is a *subtype* (implying substitutability) of X then X has no implementation. You can "inherit" some part of the implementation of X from GENERIC_X_STUFF, MIXIN_HERE, and CETI_ALPHA_FIVE. It matters not for subtyping, as long as X upholds all the interfaces (supertypes) that it is supposed to. : Now what happens if we want Z to be a subclass (inherit : implementation of) X? I'm assuming that in Sather there is no restriction : on how Z may redefine f, is that correct? And if W is intended to be : both a subtype and a subclass of X then any redefinition of f must : follow its original type semantics, but can implement it any old way. Yes. : Yes, this is appealing.... : > : >This is yet another reason to make such a design choice, either explicitly in : >your language, or in your style of class design: keep distinct : >'concrete leaf classes' which serve *only* as a locus of shared implementation : >from 'abstract classes' which serve as specification. : Lost you here. Are you talking about the subtype or subclass hierarchy? Both, but I am advocating doing them separately, as suggested by the "Design Patterns" book. : If subclass, why would the leaves share implementation with their abstract : ancestors? I would have expected abstract ancestors to be supertypes. They are. : If subtype, why mention the sharing of implementation at all? They are distinct: the "shared locus of implementation class" has nothing to do with subtyping, but is a common idiom. There is some far superior explanation on the Sather home page: http://www.icsi.berkeley.edu/~sather Look for the paper about the type & class system. : *------------------------------------------------------------------------------* : Jim McKim (860)-548-2458 Co-editor of Eiffel Outlook : Internet: jcm@hgc.edu Subscribe early and often! ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-13 0:00 ` Tucker Taft ` (2 preceding siblings ...) 1996-05-14 0:00 ` Don Harrison @ 1996-05-15 0:00 ` Alexander Kjeldaas 1996-05-15 0:00 ` Steve Tynor 1996-05-19 0:00 ` Piercarlo Grandi 5 siblings, 0 replies; 218+ messages in thread From: Alexander Kjeldaas @ 1996-05-15 0:00 UTC (permalink / raw) Robert A Duff writes: > I believe that in Eiffel, a precondition can refer to a private data > item, of which the client knows nothing. This is incorrect and would violate validity rule VAPE: "A Precondition of a routine r of class C is valid if and only if every feature whose final name appears in any Assertion_clause is available to every class to which r is available." astor Alexander Kjeldaas http://www.stud.unit.no/~astor/ Stud.techn astor@guardian.no -- Alexander Kjeldaas http://www.stud.unit.no/~astor/ Guardian Networks AS astor@guardian.no ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-13 0:00 ` Tucker Taft ` (3 preceding siblings ...) 1996-05-15 0:00 ` Alexander Kjeldaas @ 1996-05-15 0:00 ` Steve Tynor 1996-05-19 0:00 ` Piercarlo Grandi 5 siblings, 0 replies; 218+ messages in thread From: Steve Tynor @ 1996-05-15 0:00 UTC (permalink / raw) In article <DrGtq8.1zx@world.std.com> bobduff@world.std.com (Robert A Duff) writes: | In article <TYNOR.96May15090527@twratl.atlanta.twr.com>, | Steve Tynor <tynor@atlanta.twr.com> wrote: | >"Availablity" relates to the "export status" of each feature. If I | >write: | >[...explanation of VAPE deleted] | | Makes sense, but it doesn't answer my original question. Near the end | of 9.8, *after* the VAPE rule, there is a definition of "Availability of | an Assertion Clause" (which is not the same thing as availability of a | feature, although it's related). My question was, can a precondition | ever be "unavailable"? It seems to me that if it were, it would fail | VAPE, and therefore wouldn't be allowed in the first place. Am I | confused? No, I think you're just thinking too hard :-). The VAPE rule is a validity rule that the compiler can use as the justification for emitting an error. The "availability of an assertion clause" definition is a bit redundant, but not in contradiction. Note that the definition also applies to _postconditions_ (which VAPE does not address) so it's not entirely redundant. The VAPE rule could, I suppose, be reworded to use the definition of assertion availability: VAPE(revised): Each assertion clause in the precondition of a routine must be available to every class to which its feature is available. Same rule; just worded differently. Anyway, to answer your question: no since a precondition that is not available according to the definition implicitly violates VAPE. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Never express yourself more clearly than you think. -- Neils Bohr Steve Tynor Email: Steve.Tynor@atlanta.twr.com Tower Technology WWW: http://www.twr.com/ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-13 0:00 ` Tucker Taft ` (4 preceding siblings ...) 1996-05-15 0:00 ` Steve Tynor @ 1996-05-19 0:00 ` Piercarlo Grandi 5 siblings, 0 replies; 218+ messages in thread From: Piercarlo Grandi @ 1996-05-19 0:00 UTC (permalink / raw) mbk> James McKim (jcm@hgc.edu) wrote: jcm> I think Don is right in theory, but that Tucker is right in jcm> practice. In practice I regularly run into situations in which I jcm> want a concrete feature that provides default behavior for jcm> descendants, but I don't want the descendants to be totally jcm> constrained by that behavior. IOW, I'd like two distinct kinds of jcm> assertions. >>> On 18 May 1996 19:46:59 GMT, mbk@caffeine.engr.utk.edu (Matt Kennel) >>> said: mbk> Perhaps what you want is to be able to inherit implementation mbk> without also inheriting interface, which in Eiffel, encompasses mbk> assertion as well as routine signatures. Perhaps what you both really want is a language that allows independent reuse of interface, specification and implementation, that is the ability to assemble a class out of arbitrary parts from other classes, or simply from libraries of parts. This is not a difficult thing to have in an OO language: and I have been preaching about this on comp.object for several years now. Perhaps OO language designers don't read comp.object. :-) Actually, I think the reasons is that most OO languages are unfortunately still based, out of sheer historical accident, on the unfortunate ``prefixing'' model of reuse pioneered in SIMULA 67. That was around twenty five years ago, though -- but then there are still very recognizable traces of FORTRAN in many current popular languages. mbk> This is yet another reason to make such a design choice, either mbk> explicitly in your language, or in your style of class design: keep mbk> distinct 'concrete leaf classes' which serve *only* as a locus of mbk> shared implementation from 'abstract classes' which serve as mbk> specification. In existing languages, yes, there is no alternative; but it is an abuse of the ``class'' name. A class, strictly speaking, being the encapsulated implementation of an ADT, has all three of interface, implementation and specification, and an ``abstract class'' is not a class. I know it is a popular term, but let me object :-) that it is just another opportunity for the unwary/unweary to be tripped by terminology: an awful lot of people, as seen in this very newsgroup, take monikers seriously, even when they are obviously entirely unrelated to the concept they label (as, famously, in ``Object Oriented Programming''). In this respect ``class interface definition'' or perhaps just ``interface definition'' is a much better term than ``abstract class''. However, let me add that I don't criticize you too much for using it: its use is ingrained, it is even part of the official terminology of some popular languages. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-09 0:00 ` Richard Riehle 1996-05-10 0:00 ` Tucker Taft @ 1996-05-14 0:00 ` James McKim 1996-05-15 0:00 ` Juergen Schlegelmilch 1 sibling, 1 reply; 218+ messages in thread From: James McKim @ 1996-05-14 0:00 UTC (permalink / raw) In article <Pine.GSO.3.92.960509171645.1191B-100000@nunic.nu.edu> Richard Riehle <rriehle@nunic.nu.edu> writes: >On Thu, 9 May 1996, Don Harrison wrote: > >> Juergen Schlegelmilch writes: >> >> :Yes, dispatch failure for multiple dispatch indicates broken polymorphism. >> :The design of inheritance hierarchies is often not seen in the light of >> :the Liskov Substitution Principle, >> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >> Could you (or somone) explain what this is? Sorry, I'm a bit ignorant. > > > You might want to look at a paper by Liskov, "Subtype Hierarchy and > Implementation Hierarchy," Proceedings of the ACM Conference on > Object-Oriented Programming, 1987 As I recall, this paper assumes only the simplest changes in the subtypes, namely additional features and reimplementation of features for (usually) efficiency reasons. She does not (in this paper) deal with the way the _semantics_ of features may change in the subtype. > > > Though the substitutability principle is often attributed to Liskov, > it is also given treatment in a paper from Wegner and Zdonic, > "Inheritance as an Incremental Modification Mechanism" published in > the Proceedings of the European Conference on > Object-Oriented Programming, 1988. > > An important overview of this subject is Cardelli and Wegner, "On > Understanding Types, Data Abstraction, and Polymorphism," ACM > Computing Surveys, Number 17, 1985. It's been a while since I've looked at these, but again I don't believe either paper addresses how the semantics of a subtype may vary from that of its supertype. The earliest paper that I know deals with this issue in the now accepted way (weaker preconditions, stronger postconditions, stronger invariants) is [Meyer, 1987] B. Meyer, Eiffel: "Programming for Reusability and Extendibility", ACM SIGPLAN Notices, 85-94, February, 1987. which is also perhaps the first generally published discussion of Eiffel. Bertrand does not couch the discussion in terms of subtypes and substitutabily, and, of course, it is now well known that Eiffel's inheritance mechanism is not a safe subtyping mechanism, but he got the assertion part right, and to my knowledge he was the first to do so. Anyone know of an earlier ref. on this subject? [..] > > Richard Riehle > Hope this helps, -- Jim -- *------------------------------------------------------------------------------* Jim McKim (860)-548-2458 Co-editor of Eiffel Outlook Internet: jcm@hgc.edu Subscribe early and often! ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-14 0:00 ` James McKim @ 1996-05-15 0:00 ` Juergen Schlegelmilch 0 siblings, 0 replies; 218+ messages in thread From: Juergen Schlegelmilch @ 1996-05-15 0:00 UTC (permalink / raw) On Tue, 14 May 1996 19:03:22 GMT, James McKim <jcm@hgc.edu> wrote: > > It's been a while since I've looked at these, but again I don't believe > either paper addresses how the semantics of a subtype may vary from that > of its supertype. The earliest paper that I know deals with this > issue in the now accepted way (weaker preconditions, stronger postconditions, > stronger invariants) is > > [Meyer, 1987] B. Meyer, Eiffel: "Programming for Reusability and Extendibility", ACM SIGPLAN Notices, 85-94, February, 1987. I agree that this is a very important part of the picture but I also want to point out that this is not the full picture. With invariants, pre- and postconditions you fix the semantics of objects and single operations. Methods are often not designed one-by-one, but are intended to be called in certain sequences. Preconditions can be used to enforce these sequences (e.g. for files: first open(), then either any sequence of read operations, or any sequence of write operations, finally a close()), i.e. they have two different tasks: 1. protect the method against being called with wrong arguments 2. protect the object against invalid life cycles The second part could be expressed by class invariants in temporal logic to make it more explicit. There are few publications about inheritance of these life cyle conditions; I only know of one: @INPROCEEDINGS{SHJWF94, key={SHJWF94}, author={Saake, G. and Hartel, P. and Jungclaus, R. and Wieringa, R. and Feenstra, R.}, title="{Inheritance Conditions for Object Life Cycle Diagrams}", booktitle={Workshop Formale Grundlagen {f\"ur} den Entwurf von Informationssystemen, Tutzing}, editor={Lipeck, U. and Vossen, G.}, publisher={Technical Report Univ.\ Hannover, No.\ 03/94}, year=1994, pages = "79--89" } If there are more papers on this topic, please let me know. Juergen -- +-----------------------------------------------------------------------------+ Dipl.-Inf. Juergen Schlegelmilch University of Rostock email: schlegel@Informatik.Uni-Rostock.de Computer Science Department http://www.informatik.uni-rostock.de/~schlegel Database Research Group Tel: ++49 381 498 3402 18051 Rostock Fax: ++49 381 498 3404 Germany +-----------------------------------------------------------------------------+ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-08 0:00 ` Juergen Schlegelmilch [not found] ` <Dr4538.D27@assip.csasyd.oz> @ 1996-05-20 0:00 ` Joachim Durchholz 1 sibling, 0 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-05-20 0:00 UTC (permalink / raw) jcm@hgc.edu wrote 14.05.96 on Re: Real OO: > It's been a while since I've looked at these, but again I don't believe > either paper addresses how the semantics of a subtype may vary from that > of its supertype. Well, in Eiffel, it is easy to consider preconditions a part of the signature, so the Substitution Principle easily extends to them. In fact, > the now accepted way (weaker preconditions, stronger > postconditions, stronger invariants) can be derived from the SP if it is stated as "a section of code must remain valid if an object is replaced with an object of a descendant type". > it is now well known that Eiffel's > inheritance mechanism is not a safe subtyping mechanism, Do you mean the catcalls (Changed Availability or Type of routines / routine parameters / attributes)? These have been fixed (or are in the process of being fixed - might depend on exact version of compiler and language definition). I'd be very interested in any remaining holes. > but he got the > assertion part right, and to my knowledge he was the first to do so. And this is what I value most highly in Eiffel. I don't know of any other commercially available OO language that does it right (PLEASE, tell me I'm wrong!) -Joachim -- Looking for a new job. Resume available on request. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-26 0:00 ` Norman H. Cohen ` (2 preceding siblings ...) 1996-05-02 0:00 ` Joachim Durchholz @ 1996-05-07 0:00 ` Joachim Durchholz 1996-05-09 0:00 ` Don Harrison 3 siblings, 1 reply; 218+ messages in thread From: Joachim Durchholz @ 1996-05-07 0:00 UTC (permalink / raw) ncohen@watson.ibm.com wrote 06.05.96 on Re: Real OO: > |> As I understand it, you mean the multiple dispatching mechanisms > |> (a.k.a. classwide operations). > > No, that's not what classwide operatins are. Ah, now some dark spots lighten up! > (By "discriminated union," we mean that for each value V of each type in > the class, there is a corresponding value in T'Class, and that the > corresponding value in T'Class includes a "tag" identifying the type of > the original value V.) In Eiffel, each value from a class is also considered a value of its ancestor classes. The language definition does not offer a discriminating tag. (However, some libraries have a feature that will return the dynamic type of an object at run-time; this could be used as a tag if needed.) > If you write a procedure > > procedure Q(X: in A'Class); > > it is not an operation of type A, so it is not inherited in the usual > sense by types derived from A. However, in a call, the actual parameter > may belong to A'Class, A, or any type descended from A. Unlike P, Q > never dispatches. In Eiffel you'd declare Q "frozen". This means you're not allowed to override it, effectively making it non-dispatching. > The body of Q must be written > in such a way that it depends only on properties common to all types in > the class. Same in Eiffel - when writing Q, no features from descendant classes are yet available. > THERE IS NO CLOS-LIKE > MULTIPLE DISPATCH IN ADA. Glad to hear that. Sorry for bashing the wrong language. > You CAN declare > > procedure S(X: in A'Class; Y: in B'Class); > > and this poses no problems. It does not dispatch. Well, Eiffel doesn't allow you to freeze parameter classes, so we have a point where Ada allows a bit more freedom. I can't comment on this yet - I'll have to revisit everything in this thread that had the words "class-wide" in it, which will take me some time... -Joachim -- Looking for a new job. Resume available on request. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-07 0:00 ` Joachim Durchholz @ 1996-05-09 0:00 ` Don Harrison 1996-05-09 0:00 ` Jon S Anthony 1996-05-09 0:00 ` Joachim Durchholz 0 siblings, 2 replies; 218+ messages in thread From: Don Harrison @ 1996-05-09 0:00 UTC (permalink / raw) Joachim Durchholz writes: :ncohen@watson.ibm.com wrote 06.05.96 on Re: Real OO: [...] :> You CAN declare :> :> procedure S(X: in A'Class; Y: in B'Class); :> :> and this poses no problems. It does not dispatch. : :Well, Eiffel doesn't allow you to freeze parameter classes, so we have :a point where Ada allows a bit more freedom. Disagree. The fact that Eiffel allows you to redefine them to conformant types in descendants indicates greater freedom not less. There is no need to preclude redefinition and it is more flexible to be able to. BTW, I've just noticed that the third example in my previous post is not equivalent because it has a third parameter (Current): class SOME_OTHER_CLASS feature frozen q (x: X; y: B) is ... -- X and Y are classwide operands :-Joachim : :-- :Looking for a new job. Resume available on request. -- /// Don. (o o) =-=-=-=-=-=-=-=oOO=-(_)-=OOo=-=-=-=-=-=-=-=- Don Harrison donh@syd.csa.com.au ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-05-09 0:00 ` Don Harrison @ 1996-05-09 0:00 ` Jon S Anthony 1996-05-09 0:00 ` Joachim Durchholz 1 sibling, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-05-09 0:00 UTC (permalink / raw) In article <Dr4qpz.FMx@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: > :Well, Eiffel doesn't allow you to freeze parameter classes, so we have > :a point where Ada allows a bit more freedom. > > Disagree. The fact that Eiffel allows you to redefine them to conformant > types in descendants indicates greater freedom not less. There is no need to > preclude redefinition and it is more flexible to be able to. For good or ill, you can redefine classwide operations to your hearts content. And you can do it pretty much wherever and however you want - even as a local/nested operation of another subprogram (of any sort). I see no "greater" freedom in the Eiffel model at all. If anything it is more restrictive on several counts. What are you thinking of when you make the claim that it has "greater freedom"? /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] 218+ messages in thread
* Re: Real OO 1996-05-09 0:00 ` Don Harrison 1996-05-09 0:00 ` Jon S Anthony @ 1996-05-09 0:00 ` Joachim Durchholz 1 sibling, 0 replies; 218+ messages in thread From: Joachim Durchholz @ 1996-05-09 0:00 UTC (permalink / raw) donh@syd.csa.com.au wrote 09.05.96 on Re: Real OO: > :Well, Eiffel doesn't allow you to freeze parameter classes, so we have > :a point where Ada allows a bit more freedom. > > Disagree. The fact that Eiffel allows you to redefine them to conformant > types in descendants indicates greater freedom not less. There is no need to > preclude redefinition and it is more flexible to be able to. Depends on point of view. In Ada, I have more freedom in that I can specify more restrictions <g>... More generally speaking, just because there is more freedom for programmers to use a routine, this automatically means the author of the routine is forced to be less restrictive about his parameters. Of course, this is not a problem in Eiffel - the universally accepted rule is "wherever a class is named, a descendant will do" (with some notable exceptions, but these are intended for special cases and not for normal use). Still, I don't have a good Eiffel variant of the corresponding_parts_equal routine earlier in this thread. Any takers? -Joachim -- Looking for a new job. Resume available on request. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Detecting type mismatch at compile time (was Re: Real OO) 1996-03-23 0:00 ` Joachim Durchholz 1996-03-26 0:00 ` Norman H. Cohen @ 1996-04-02 0:00 ` Robert I. Eachus 1996-04-03 0:00 ` Richard Bielak 1996-04-04 0:00 ` Don Harrison 1 sibling, 2 replies; 218+ messages in thread From: Robert I. Eachus @ 1996-04-02 0:00 UTC (permalink / raw) In article <65lDeVZF3RB@herold.franken.de> jhd@herold.franken.de (Joachim Durchholz) writes: > > Typically, a programmer comes upon this knowledge (that the dynamic type > > is exactly the specific type) because it is an obvious consequence of the > > algorithm, not because he has performed a sophsiticated data flow > > analysis by hand! If an Ada programmer is mistaken, he will be told so > > by the failure of a tag check, at precisely the site of the attempt to > > assign or pass a value with one dynamic type to a variable declared to be > > of some different specific type. No hunting necessary. > My (somewhat dogmatic) belief is that a program shouldn't fail > with a typing error at run-time. Types are a mechanism introduced > to allow the compiler to catch errors. This is a pure opinion, > based on nothing more than the void in my head ;) - but I think I > could justify it, though with much handwaving. But the case being discussed above is exactly the one that can't be detected until run-time. For example, I can have a fruit class: type Fruit is abstract tagged null record; -- and some member subclasses: type Apple is new Fruit with private; type Orange is new Fruit with private; -- a type that designates any Fruit: type Fruit_Pointer is access Fruit'Class; -- now I make an array of Fruit_Pointers: type Fruit_Arrangement is array (Integer range <>) of Fruit_Pointer; -- and an instance of Fruit_Arrangement: FA: Fruit_Arrangement(1..10); Now at execution time I put some apples and some oranges in my fruit arrangement, then do: S := FA(x) + FA(y); It is only at run-time that I know whether I am trying to add apples and oranges. In the corresponding case, where the type of the operands can be determined at compile time, the error would be diagnosed at compile time. (Unless, of course, the programmer had explicitly defined an operation to add apples to oranges. ;-) Maybe that smiley is misplaced. There is a technique in Ada 95 for cases where you do want to support heterogeneous operations. The fully general case often requires declaring several primitive operations for each of the "visible" operations, but the others can be private, and in some cases never need to be overridden. In the much more common case where the result type is always the same we could define: function "+"(L,R: Fruit'Class) return Fruit_Arrangement; function "+"(L: Fruit_Arrangement; R: Fruit'Class) return Fruit_Arrangement; function "+"(L: Fruit'Class; R: Fruit_Arrangement) return Fruit_Arrangement; function "+"(L,R: Fruit_Arrangement) return Fruit_Arrangement; The body of the first plus is obviously: function "+"(L,R: Fruit'Class) return Fruit_Arrangement is begin return Fruit_Arrangement'(1 => new Fruit'Class(L), 2 => new Fruit'Class(R)); end "+"; -- Robert I. Eachus with Standard_Disclaimer; use Standard_Disclaimer; function Message (Text: in Clever_Ideas) return Better_Ideas is... ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Detecting type mismatch at compile time (was Re: Real OO) 1996-04-02 0:00 ` Detecting type mismatch at compile time (was Re: Real OO) Robert I. Eachus @ 1996-04-03 0:00 ` Richard Bielak 1996-04-04 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Richard Bielak @ 1996-04-03 0:00 UTC (permalink / raw) In article <EACHUS.96Apr2184154@spectre.mitre.org>, Robert I. Eachus <eachus@spectre.mitre.org> wrote: [...] > > Now at execution time I put some apples and some oranges in my >fruit arrangement, then do: > > S := FA(x) + FA(y); > > It is only at run-time that I know whether I am trying to add >apples and oranges. You should have written: if FA(x) > FA(y) then ... so only at runtime you would know if you are comparing apples and oranges! ;-) ...richie (sorry, couldn't resist) -- * richieb@ritz.mordor.com - at home | Richie Bielak * * richieb@calfp.com - at work | * * >> If it were readable, it wouldn't be called "code". (me) << * *------------------------------------------------------------------* ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Detecting type mismatch at compile time (was Re: Real OO) 1996-04-02 0:00 ` Detecting type mismatch at compile time (was Re: Real OO) Robert I. Eachus 1996-04-03 0:00 ` Richard Bielak @ 1996-04-04 0:00 ` Don Harrison 1 sibling, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-04-04 0:00 UTC (permalink / raw) Robert I. Eachus) wrote: :In article <65lDeVZF3RB@herold.franken.de> jhd@herold.franken.de (Joachim Durchholz) writes: : > My (somewhat dogmatic) belief is that a program shouldn't fail : > with a typing error at run-time. Types are a mechanism introduced : > to allow the compiler to catch errors. This is a pure opinion, : > based on nothing more than the void in my head ;) - but I think I : > could justify it, though with much handwaving. Agree. I suppose the Eiffel approach would be for the client to pretest compatibility before making the call (as you could in Ada) and preserve the integrity of the target with a precondition requiring the dynamic types (feature 'dynamic_type' of class INTERNAL) of Current and the parameter are compatible. This protection is stronger than Ada's because the point of failure is BEFORE the body of the routine is executed rather than WHILE it is executed. : But the case being discussed above is exactly the one that can't be :detected until run-time. For example, I can have a fruit class: Equivalent Eiffel interspersed (omitting semicolons because they are optional): : type Fruit is abstract tagged null record; class FRUIT ... end :-- and some member subclasses: : : type Apple is new Fruit with private; : type Orange is new Fruit with private; class APPLE inherit FRUIT ... end class ORANGE inherit FRUIT ... end :-- a type that designates any Fruit: : : type Fruit_Pointer is access Fruit'Class; Don't need this due to Eiffel's default reference semantics. :-- now I make an array of Fruit_Pointers: : : type Fruit_Arrangement is array (Integer range <>) of Fruit_Pointer; class FRUIT_ARRANGEMENT inherit ARRAY [FRUIT] ... end :-- and an instance of Fruit_Arrangement: : : FA: Fruit_Arrangement(1..10); fa: FRUIT_ARRANGEMENT Don't need to constrain size of arrangement. If it is implemented as an array, the size may be changed dynamically (with a performance penalty; obviously, if the arrangement is changing often, you would use something other than an array). : Now at execution time I put some apples and some oranges in my :fruit arrangement, then do: : : S := FA(x) + FA(y); s.add_fruit (fa@x, fa@y) (Of course, to faithfully model the situation, you would first remove fruit from one arrangement before adding them to another). : It is only at run-time that I know whether I am trying to add :apples and oranges. In the corresponding case, where the type of the :operands can be determined at compile time, the error would be :diagnosed at compile time. (Unless, of course, the programmer had :explicitly defined an operation to add apples to oranges. ;-) Likewise with Eiffel. : Maybe that smiley is misplaced. There is a technique in Ada 95 for :cases where you do want to support heterogeneous operations. The :fully general case often requires declaring several primitive :operations for each of the "visible" operations, but the others can be :private, and in some cases never need to be overridden. In the much :more common case where the result type is always the same we could :define: : : function "+"(L,R: Fruit'Class) return Fruit_Arrangement; : function "+"(L: Fruit_Arrangement; R: Fruit'Class) return : Fruit_Arrangement; : function "+"(L: Fruit'Class; R: Fruit_Arrangement) return : Fruit_Arrangement; : function "+"(L,R: Fruit_Arrangement) return Fruit_Arrangement; : : The body of the first plus is obviously: : : function "+"(L,R: Fruit'Class) return Fruit_Arrangement is : begin : return : Fruit_Arrangement'(1 => new Fruit'Class(L), 2 => new Fruit'Class(R)); : end "+"; add_fruit (f: FRUIT) is ... end add_arrangement (a: FRUIT_ARRANGEMENT) is ... end make (f1, f2: FRUIT) is ... end : : Robert I. Eachus Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-22 0:00 ` Don Harrison ` (2 preceding siblings ...) 1996-03-23 0:00 ` Joachim Durchholz @ 1996-03-28 0:00 ` Joachim Durchholz 1996-03-29 0:00 ` Norman H. Cohen 3 siblings, 1 reply; 218+ messages in thread From: Joachim Durchholz @ 1996-03-28 0:00 UTC (permalink / raw) ncohen@watson.ibm.com wrote 26.03.96 on Re: Real OO: > The Ada rule under discussion--that a subprogram call is not dispatching > unless the relevant ACTUAL parameters belong to a classwide type--does > not limit the called routine at all. It provides the opportunity for the > CALLER, calling from a context in which the dynamic type is known, to > declare this knowledge. The Eiffel optimization you describe is a > difficult one because in practice it is difficult for an optimizing > compiler to resolve pointer-induced aliasing and determine the dynamic > type at compile time. Eiffel references ( = pointers for this discussion) always have a static type that can be inferred by the compiler. A pointer might be more restricted, but then it should be declared to be of the more restricted type in the beginning. Pointer-induced aliasing is possible in Eiffel (and in fact creates problems in other areas), but even then the referred-to objects is 100% guaranteed to conform to the declared type. No data-flow analysis necessary. > The analysis required is expensive, requiring > interprocedural analysis to be effective, True. And one of the places where a typical Eiffel compiler would spend time. But I don't think this is a real problem - the Eiffel compiler will spend less time than the programmer thinking about wether he can really assume what he's telling the compiler. (And even less time if the programmer goes bug-hunting if the programmer's assumption was wrong.) > and the payoff > is rare, so it is not clear that it is worthwhile for an optimizing > compiler to perform this analysis routinely. My Booch says that it is usually possible to replace 80% of all calls by static (non-polymorphic) ones. If I can guarantee that I catch all cases, this is worthwile. (And Eiffel allows me to statically determine the set of possible dynamic types, so no problem there.) > In Ada, the programmer > conveys, not through any special hint-giving pragma, but just through the > natural course of declaring an object to belong to a specific type, that > a call should not be dispatching. In Eiffel, the programmer "declares" an object to belong to a specific type by not assigning any object of a descendant type to it. I think this is less error-prone and requires less thinking about an otherwise uninteresting detail. > |> > Making it classwide simply documents the fact that the algorithm does > |> > not depend on the tag of the parameter, and causes the compiler to > |> > catch any attempt to override. > |> > |> This assumes that the writer of a module knows that his routines never > |> need to be overridden. > > No, it assumes that the writer of a module knows that a particular > routine in the module accomplishes its purpose without directly > exploiting knowledge of a given object's dynamic type, or tag. (The > routine may invoke other routines that dispatch based on the dynamic > type, but it does not itself differentiate among different dyanamic > types.) Sorry to object, but it *does* assume that the routine will never be overridden. This includes cases where the algorithm in the routine isn't appropriate for some descendant type - if the routine can't be changed (overridden) then, the whole class will have to be implemented. > A classwide routine is, by definition, one whose > correctness does not depend on the dynamic types of the objects it is > dealing with. True. But then, how do I know that? I think cases where I'm sure that this will hold are very rare. And there is always the possibility that somebody will extend the class in a way that I never anticipated, and in such a case, it is quite likely that routines that I considered classwide are anything but classwide in the new design. Besides, I don't think that programmer Joe Average will be able to make this distinction, especially not if the deadline has been yesterday. > ... > That's a rather limited view of the utility of OO techniques! Even fixed > and well understood tasks benefit from data abstraction and > encapsulation, from the use of inheritance to avoid redundant effort, and > from the use of polymorphism to facilitate generalized algorithms. Agreed. Though this won't persuade a hard-headed Structured Programming proponent. Hell, IBM successfully markets RPG, which isn't even structured, and lots of programs are being written in it. > Concerning dispatching based on multiple parameters: > > |> The type of dispatching noted here can easily be achieved with anchored > |> types in Eiffel. In an Ada-style notation, this would look as follows: > |> > |> function Corresponding_Parts_Equal > |> (Self: Shape_Type; Other: like Self) > |> return Boolean > |> > |> The "like Self" part requires Other to have the same dynamic type as > |> Self, or a type that is a descendant of Self's type. > > (Are you sure it's the DYNAMIC type? Section 11.4.3 of Meyer's > _Object_Oriented_Software_Construction_ seems to suggest that it's the > declared type.) Yes. It follows from the fact that the definition is automatically rewritten in each inheriting class. I.e. 'like Current' makes sure that dynamic and static types always match, because it causes the definition to be implicitly rewritten for each descendant class by the compiler. (Of course the compiler doesn't generate code for this in descendant classes; after all, the descendant types are just pointers behind the scene, so the original routine will still work.) > The following calls violate the requirement that the dynamic type of > Other be descended (in zero or more steps) from the dynamic type of Self, > and presumably result in a run-time error: > > FC.Corresponding_Parts_Equal(C) > HC.Corresponding_Parts_Equal(C) > FC.Corresponding_Parts_Equal(HC) > HC.Corresponding_Parts_Equal(FC) Yes, these calls are invalid. (Though they won't cause run-time errors - the compiler will find and flag them. It can do that because it knows the static type of the left-hand object and so knows the static type of the formal parameter for that call.) Umm <blush> I'm just noting this exposes a fatal flaw in my "anchored" 'like Shape_Type' definition. Let's say I'm keeping a list of Shape_Type objects (so that the compiler won't know the dynamic subtypes), the call to Corresponding_Parts_Equal would be rejected by the compiler (this is suitably named a 'catcall', 'cat' meaning 'changed availability or type - the 'like Current' anchor redefines the type for all descendant classes of Shape_Type, resulting just in this type of problem). So I have to shamefully retract my proposal of using 'like Current'. > The problem with this assymetry is not merely aesthetic: It greatly > complicates the programming of a routine designed to exploit the "or a > type that is a descendant of" property of Corresponding_Parts_Equal, > because that routine must determine which object's type, if any, is at > least as general as the other object's type. True. And determining which of two objects is more general makes Eiffel code that needs some heavy commenting (i.e. that is ugly, obfuscated, and inelegant). > type Graphic_Type is tagged > record > Shape : Shape_Type; > Fill : Fill_Type; > end record; > If one is interested in congruence of Graphic_Type instances, that's a > very different notion from congruence of abstract geometric figures, but > it's easily implemented as a classwide operation: > > function Congruent > (Graphic_1, Graphic_2: Graphic_Type'Class) return Boolean is > begin > return Congruent (Graphic_1.Shape, Graphic_2.Shape); > end Congruent; Plus redefining all routines of Shape_Type for Graphic_Type, with similar one-liners. And the Graphic_Type class would have to be revisited whenever the Shape_Type class is extended with new functionality, to include the new features. I'd think this is a classical example for when to use inheritance <g>. > Nonetheless, let's persue the consequences of trying to extend > Circle_Type (a descendant of Shape_Type) to include such subclasses as > Filled_Circle_Type and Hatched_Circle_Type. > > ... > > |> In addition, the Congruent code will break - Shape1'Tag and Shape2'Tag > |> will not be equal, making Congruent return false even if a Filled_Circle > |> and a Hatched_Circle actually have equal corresponding parts. > > True. If I had intended to allow shapes to be specialized to include > nongeometric features, I would add a new abstract method to Shape_Type: > > function Geometry (Special_Shape: Shape_Type) return Shape_Type'Class > is abstract; > > I would override it for Circle_Type as follows: > > function Geometry (Special_Circle: Circle_Type) return Shape_Type'Class > is Result: Circle_Type := Special_Circle; > begin > return Result; > end Geometry; > > This function always returns an object whose dynamic type, or tag, is > that of Circle_Type. Types extending Circle_Type would inherit this > function without overriding it. Thus if FC is a Shape_Type'Class object > with the dynamic type (tag) of Filled_Circle_Type, Geometry(FC) would > dispatch to the function body above and would return the corresponding > (truncated) Circle_Type object. Similar definitions of Geometry would be > given for all types in the hierarchy containing only geometric > information (e.g., Rectangle_Type, but not Filled_Rectangle_Type). > > The Congruent function would be rewritten as follows: > > function Congruent (Shape_1, Shape_2: Shape_Type'Class) return Boolean is > Shape_1_Geometry : Shape_Type'Class := Geometry(Shape_1); > Shape_2_Geometry : Shape_Type'Class := Geometry(Shape_2); > begin > return > Shape_1_Geometry'Tag = Shape_2_Geometry'Tag and then > Corresponding_Parts_Equal(Shape_1_Geometry, Shape_2_Geometry); > end Congruent; > > This function will return True when invoked with a filled circle and a > hatched circle having the same radius. I'm not sure whether this has a problem. I feel a bit uneasy about it, but I can't quite put my finger on it. I suspect this might cause problem later, and in cases that go beyond the simple example we're discussing here, but I'm not sure. Still, I have to admit it does the job, in a way that I wouldn't have thought of. -Joachim -- Im speaking for myself here. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-28 0:00 ` Real OO Joachim Durchholz @ 1996-03-29 0:00 ` Norman H. Cohen 1996-03-30 0:00 ` John G. Volan 0 siblings, 1 reply; 218+ messages in thread From: Norman H. Cohen @ 1996-03-29 0:00 UTC (permalink / raw) In article <65h8jGx-3RB@herold.franken.de>, jhd@herold.franken.de (Joachim Durchholz) writes: |> Eiffel references ( = pointers for this discussion) always have a static |> type that can be inferred by the compiler. A pointer might be more |> restricted, but then it should be declared to be of the more restricted |> type in the beginning. |> Pointer-induced aliasing is possible in Eiffel (and in fact creates |> problems in other areas), but even then the referred-to objects is 100% |> guaranteed to conform to the declared type. |> No data-flow analysis necessary. The data-flow analysis is required to perform optimizations based on knowledge that an objects dynamic type is precisely its static type AND NOT some more restricted type. There is no way to express this knowledge with an Eiffel declaration. |> > The analysis required is expensive, requiring |> > interprocedural analysis to be effective, |> |> True. And one of the places where a typical Eiffel compiler would spend |> time. But I don't think this is a real problem - the Eiffel compiler will |> spend less time than the programmer thinking about wether he can really |> assume what he's telling the compiler. (And even less time if the |> programmer goes bug-hunting if the programmer's assumption was wrong.) Typically, a programmer comes upon this knowledge (that the dynamic type is exactly the specific type) because it is an obvious consequence of the algorithm, not because he has performed a sophsiticated data flow analysis by hand! If an Ada programmer is mistaken, he will be told so by the failure of a tag check, at precisely the site of the attempt to assign or pass a value with one dynamic type to a variable declared to be of some different specific type. No hunting necessary. |> |> > and the payoff |> > is rare, so it is not clear that it is worthwhile for an optimizing |> > compiler to perform this analysis routinely. |> |> My Booch says that it is usually possible to replace 80% of all calls by |> static (non-polymorphic) ones. If I can guarantee that I catch all cases, |> this is worthwile. (And Eiffel allows me to statically determine the set |> of possible dynamic types, so no problem there.) Well that depends largely on your Booch's coding style. The results presented by Calder and Grunwald in their 1994 POPL paper are different. Among other things, they measured: 1. the percentage of indirect function calls executed during their experiments that invoked methods that were never overridden (a condition that it is, in principle, straightforward to detect at link time, but not sooner) 2. the percentage of indirect function calls executed during their experiments at call sites from which only one target was invoked during THAT execution of the program (Notice that in each case they counted executions of calls, not the number of call sites in the text of the program.) Some of the calls counted in Measurement 2 may have been from call sites where a sophisticated interprocedural data flow analysis would have detected that only one method version was reachable; but some of these calls may have been from call sites that would have invoked different method versions in other executions of the program, with different input data. Calder and Grunwald describe Measurement 2 as an upper bound on the number of dynamic calls that could be turned into static calls by powerful interprocedural optimizations, and Measurement 1 as the lower bound we would expect of such optimizations, since that much improvement can be achieved even by a naive interprocedural optimization. The mean values, over the programs they measured, were 31.6% for Measurement 1 and 66.3% for Measurement 2, a far cry from your Booch's 80%. Of course in Eiffel programs, even call sites where there is obviously one target are necessarily programmed with indirect function calls, so we would expect these percentages to be higher. In Ada programs, call sites where there is obviously one target are more likely to be programmed as direct calls in the first place, so these percentages (in which the denominator is the number of calls at the remaining dispatching call sites) are likely to be lower. In other words, the optimization will seem more "worthwhile" to Eiffel programmers than to Ada programmers because Eiffel programs need it more badly. |> > In Ada, the programmer |> > conveys, not through any special hint-giving pragma, but just through the |> > natural course of declaring an object to belong to a specific type, that |> > a call should not be dispatching. |> |> In Eiffel, the programmer "declares" an object to belong to a specific |> type by not assigning any object of a descendant type to it. I think this |> is less error-prone and requires less thinking about an otherwise |> uninteresting detail. Of course this is not a declaration at all. It is a run-time property that may or may not be determinable at compile time; and if determinable at all, may be determinable only by sophisticated analysis. I wrote: |> > |> > Making it classwide simply documents the fact that the algorithm does |> > |> > not depend on the tag of the parameter, and causes the compiler to |> > |> > catch any attempt to override. Someone (Joachim?) responded: |> > |> This assumes that the writer of a module knows that his routines never |> > |> need to be overridden. I responded: |> > No, it assumes that the writer of a module knows that a particular |> > routine in the module accomplishes its purpose without directly |> > exploiting knowledge of a given object's dynamic type, or tag. (The |> > routine may invoke other routines that dispatch based on the dynamic |> > type, but it does not itself differentiate among different dyanamic |> > types.) Joachim responded: |> Sorry to object, but it *does* assume that the routine will never be |> overridden. This includes cases where the algorithm in the routine isn't |> appropriate for some descendant type - if the routine can't be changed |> (overridden) then, the whole class will have to be implemented. But the routine was written in the first place not to rely on distinctive properties of different descendants, so the algorithm must be appropriate for any new descendant type. In a previous post, I wrote |> > A classwide routine is, by definition, one whose |> > correctness does not depend on the dynamic types of the objects it is |> > dealing with. and Joachim responded: |> True. But then, how do I know that? I think cases where I'm sure that this |> will hold are very rare. And there is always the possibility that somebody |> will extend the class in a way that I never anticipated, and in such a |> case, it is quite likely that routines that I considered classwide are |> anything but classwide in the new design. Formally, you know that because the correctness of the classwide routine depends only on the specifications (preconditions and postconditions) of the (root versions of) the methods it invokes. Proper OO methodology, explicitly codified in Eiffel, requires that any overriding versions of these methods preserve, and perhaps strengthen, these specifications. Opportunities for classwide subprograms are not rare at all. Here is an example: There is an abstract type (in Eiffel terms, a deferred class) for bank transactions. It has descendant types for different kinds of transactions (deposits, checks, withdrawals, fees, interest, etc.). Among the methods of the root type are classwide functions returning the date and amount of the transaction (classwide because they simply examine components present in all descendants of the type) and a dispatching function returning a string that describes the transaction (dispatching because the information in the string, and the algorithm for formatting it, depend on the kind of transaction). By calling these three methods, one can write a classwide routine that takes a transaction of unknown dynamic type and a previous balance, prints a line of a monthly statement corresponding to that transaction, and updates the running balance. (This routine can be called in a loop to print a monthly statement, given an array of transactions.) The classwide routine's correctness clearly does not depend on the dynamic type of the transaction with which it is invoked. The addition of a new kind of transaction cannot possibly break the classwide routine (unless, of course, the type for the new kind of transaction overrides the description-string function with a broken version that returns, say, the lyrics of Waltzing Matilda instead of a description of the transaction). In a previous post, I suggested that, given a type Shape_Type meant to represent abstract Euclidean entities, the best way to represent graphic representations of shapes was by composition rather than inheritance: |> > type Graphic_Type is tagged |> > record |> > Shape : Shape_Type; |> > Fill : Fill_Type; |> > end record; (Actually, that should have been: type Shape_Pointer_Type is access Shape_Type'Class; type Graphic_Type is tagged record Shape : Shape_Pointer_Type; Fill : Fill_Type; end record; so that a Graphic_Type instance can contain--or, as we must make explicit in Ada, point to--any kind of shape.) I wrote: |> > If one is interested in congruence of Graphic_Type instances, that's a |> > very different notion from congruence of abstract geometric figures, but |> > it's easily implemented as a classwide operation: |> > |> > function Congruent |> > (Graphic_1, Graphic_2: Graphic_Type'Class) return Boolean is |> > begin |> > return Congruent (Graphic_1.Shape, Graphic_2.Shape); |> > end Congruent; Joachim repsonded: |> Plus redefining all routines of Shape_Type for Graphic_Type, with similar |> one-liners. And the Graphic_Type class would have to be revisited whenever |> the Shape_Type class is extended with new functionality, to include the |> new features. You're right, if the application requires Graphic_Type to reflect all the features of Shape_Type, not just the Congruent function, this solution is inappropriate. |> I'd think this is a classical example for when to use inheritance <g>. No, the proper solution is to add the classwide function function Geometry_Of (Graphic: Graphic_Type'Class) return Shape_Pointer_Type is begin return Graphic.Shape; end Geometry_Of; (and possibly a Set_Geometry_Of procedure). Then, to test whether Graphic_Type instances G1 and G2 have congruent polygons, we write Congruent (Geometry_Of(G1).all, Geometry_Of(G2).all) -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-29 0:00 ` Norman H. Cohen @ 1996-03-30 0:00 ` John G. Volan 0 siblings, 0 replies; 218+ messages in thread From: John G. Volan @ 1996-03-30 0:00 UTC (permalink / raw) In article <4jh5cr$101s@watnews1.watson.ibm.com> Norman H. Cohen, ncohen@watson.ibm.com writes: >No, the proper solution is to add the classwide function > > function Geometry_Of > (Graphic: Graphic_Type'Class) return Shape_Pointer_Type is > begin > return Graphic.Shape; > end Geometry_Of; > >(and possibly a Set_Geometry_Of procedure). Then, to test whether >Graphic_Type instances G1 and G2 have congruent polygons, we write > > Congruent (Geometry_Of(G1).all, Geometry_Of(G2).all) Actually, I think you might be able get by with: function Geometry_Of (Graphic: Graphic_Type'Class) return Shape_Type'Class is begin return Graphic.Shape.all; end Geometry_Of; I believe this would be a return-by-reference function, so I don't think there would be any extra copying involved. Of course, this only provides a "constant" view of the referenced Shape, so you couldn't then pass it into an "in out" parameter for instance. But you would be able to do: Congruent (Geometry_Of(G1), Geometry_Of(G2)) Note you could use the result of a call to Geometry_Of in this way even if Shape_Type happened to be limited (although you wouldn't be able to save the result in a variable, for instance). ------------------------------------------------------------------------ Internet.Usenet.Put_Signature ( Name => "John G. Volan", E_Mail => "John_Volan@dayton.saic.com", Favorite_Slogan => "Ada95: The *FIRST* International-Standard OOPL", Humorous_Disclaimer => "These opinions are undefined by SAIC, so" & "any use would be erroneous ... or is that a bounded error now?" ); ------------------------------------------------------------------------ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO [not found] <4id031$cf9@dayuc.dayton.saic.com> ` (2 preceding siblings ...) 1996-03-22 0:00 ` Don Harrison @ 1996-03-26 0:00 ` Jon S Anthony 1996-03-29 0:00 ` Joachim Durchholz 4 siblings, 0 replies; 218+ messages in thread From: Jon S Anthony @ 1996-03-26 0:00 UTC (permalink / raw) In article <leschkes.827424395@ferret> leschkes@ferret.cig.mot.com (Scott Leschke) writes: > How does one declare syntactically that an operation is SPECIFIC to a > type within a class, as opposed to being either a primitive of that > type or class-wide? A couple of "obvious" ways: package P is type T is tagged... -- various primitive ops... package Tspecific is -- various ops of _specific_ type T -- Not in immediate scope of package -- declaring T so not primitive either. end Tspecific; end P; or package P.Tspecific is -- various ops of _specific_ type T -- Not in immediate scope of package -- declaring T so not primitive either. end P.Tspecific; > I've also wondered if there was any way to explicitly declare an operation > as invariant within a class and hence, non-overridable. Why don't root level classwide operations work? For example: type T is tagged ... -- various derivations procedure Tinvariant (o : T'Class); -- -- Will eat anything in T'Class tree. -- Not primitive so not inheritable/overidable -- Not really overloadable either. /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] 218+ messages in thread
* Re: Real OO [not found] <4id031$cf9@dayuc.dayton.saic.com> ` (3 preceding siblings ...) 1996-03-26 0:00 ` Jon S Anthony @ 1996-03-29 0:00 ` Joachim Durchholz 1996-04-04 0:00 ` Don Harrison 4 siblings, 1 reply; 218+ messages in thread From: Joachim Durchholz @ 1996-03-29 0:00 UTC (permalink / raw) Gosh, this thread is becoming quite verbose. And I'm losing some ground... but let's see what remains of my points ;) . > Typically, a programmer comes upon this knowledge (that the dynamic type > is exactly the specific type) because it is an obvious consequence of the > algorithm, not because he has performed a sophsiticated data flow > analysis by hand! If an Ada programmer is mistaken, he will be told so > by the failure of a tag check, at precisely the site of the attempt to > assign or pass a value with one dynamic type to a variable declared to be > of some different specific type. No hunting necessary. My (somewhat dogmatic) belief is that a program shouldn't fail with a typing error at run-time. Types are a mechanism introduced to allow the compiler to catch errors. This is a pure opinion, based on nothing more than the void in my head ;) - but I think I could justify it, though with much handwaving. > The results > presented by Calder and Grunwald in their 1994 POPL paper are different. > Among other things, they measured: > > 1. the percentage of indirect function calls executed during their > experiments that invoked methods that were never overridden > (a condition that it is, in principle, straightforward to detect > at link time, but not sooner) > > 2. the percentage of indirect function calls executed during their > experiments at call sites from which only one target was invoked > during THAT execution of the program > > ... > > Calder and > Grunwald describe Measurement 2 as an upper bound on the number of > dynamic calls that could be turned into static calls by powerful > interprocedural optimizations, and Measurement 1 as the lower bound we > would expect of such optimizations, since that much improvement can be > achieved even by a naive interprocedural optimization. The mean values, > over the programs they measured, were 31.6% for Measurement 1 and 66.3% > for Measurement 2, a far cry from your Booch's 80%. Umm, I cannot really comment on this - don't have such detailed statistics. Anyway, I'd be interested to know how many of the Measurement 2 calls that were not in Measurement 1 would have been present in an Eiffel program. I.e. to actually determine the utility of explicit frozen (= final) declarations, it would be necessary to determine 1) how many calls aren't made static by the Eiffel compiler that could have been made static 2) how many calls that would have been made static by the developer would lead to a run-time error or make the life of other developers more difficult. I don't see an easy way to give solid numbers. The first criterion is one that vanishes as soon as it is measured (any tool that measured the number could immediately be converted to a data-flow analysis phase of the compiler), and the second one cannot be determined without introducing ideological debate. The Eiffel standpoint is that such optimizations introduce a source of errors, and that they can be done by the compiler (in the light of the above argument, it would be "*most* can be done by the compiler"). This is a standpoint that influences many language decisions, not just the question of static (early) binding. In effect, these decisions take a lot of burden from the application programmer and put them upon the compiler writer (which makes writing a good Eiffel compiler considerably harder - but this is more of a disadvantage wrt. C++ than Ada). > In other words, the optimization will > seem more "worthwhile" to Eiffel programmers than to Ada programmers > because Eiffel programs need it more badly. Agreed. > |> In Eiffel, the programmer "declares" an object to belong to a specific > |> type by not assigning any object of a descendant type to it. I think this > |> is less error-prone and requires less thinking about an otherwise > |> uninteresting detail. > > Of course this is not a declaration at all. It is a run-time property > that may or may not be determinable at compile time; and if determinable > at all, may be determinable only by sophisticated analysis. I think most cases can be determined without doing any semantic analysis. This simply because I think most such declarations are routinely applied by the programmer, using standard practice, which should be just the cases that can be statically determined. This argument will, of course, only hold as long as I assume that the 33% difference between measurements 1 and 2 is specific to C++. One should really do such a measurement with an Eiffel program. Another source of non-optimizations may be the cases where there is a polymorphic function with a small and fixed number of variants. It may be worthwile to replace this function by a set of non-polymorphic functions that are statically linked. But that's probably something that's an issue for research right now. > In a previous post, I wrote > > |> > A classwide routine is, by definition, one whose > |> > correctness does not depend on the dynamic types of the objects it is > |> > dealing with. > > and Joachim responded: > > |> True. But then, how do I know that? I think cases where I'm sure that > |> this will hold are very rare. And there is always the possibility that > |> somebody will extend the class in a way that I never anticipated, and in > |> such a case, it is quite likely that routines that I considered classwide > |> are anything but classwide in the new design. > > Formally, you know that because the correctness of the classwide routine > depends only on the specifications (preconditions and postconditions) of > the (root versions of) the methods it invokes. Agreed. But sometimes I want to replace a routine for efficiency reasons, without altering the outside semantics (e.g. because I can utilize a better data structure in a descendant that wasn't available in the parent). > No, the proper solution is to add the classwide function > > function Geometry_Of > (Graphic: Graphic_Type'Class) return Shape_Pointer_Type is > begin > return Graphic.Shape; > end Geometry_Of; > > (and possibly a Set_Geometry_Of procedure). Then, to test whether > Graphic_Type instances G1 and G2 have congruent polygons, we write > > Congruent (Geometry_Of(G1).all, Geometry_Of(G2).all) Yes, this is a valid solution. Still, it requires me to use a client relationship, for purely technical reasons. Example: Image there is an existing system of several zillion lines that uses the Shape_Type class, but without the filled shapes. For reasons that cannot be discussed away (i.e. usually for customer demand <g>) the existing class structure has to be extended with the filled shapes. The above solution would require me to rewrite lots of calls to Congruent (and many other routines). To get away from the specifics of the Shape_Type example, I'd like to take a step back and take a broader look at the problem: Let's have a base class BASE with some equivalence relation BASE.equivalent(BASE): BOOLEAN. In some descendant types, the equivalence conditions will be modified. The equivalence conditions will vary among the descendants of BASE: Some will have conditions that differ from those of BASE, others will share their conditions with other classes in the descendance graph. In the case of shared conditions, the equivalent() routine should be declared only in the base class of the class group and inherited in the other classes. When comparing elements of groups that don't have identical equivalence conditions, the comparison may either always return false or - if the classes inherit from each other - the objects may be compared according to the equivalent() routine of the class that is higher up in the class hierarchy. I can't give an implementation of this in Eiffel right now - it's too late already. I'll give it a try this weekend. Anybody who's more awake right now is invited to implement this <g>. -Joachim -- Im speaking for myself here. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-03-29 0:00 ` Joachim Durchholz @ 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Dominique Colnet ` (4 more replies) 0 siblings, 5 replies; 218+ messages in thread From: Don Harrison @ 1996-04-04 0:00 UTC (permalink / raw) Norman wrote (responding to Joachim): :> The results :> presented by Calder and Grunwald in their 1994 POPL paper are different. :> Among other things, they measured: :> :> 1. the percentage of indirect function calls executed during their :> experiments that invoked methods that were never overridden :> (a condition that it is, in principle, straightforward to detect :> at link time, but not sooner) :> :> 2. the percentage of indirect function calls executed during their :> experiments at call sites from which only one target was invoked :> during THAT execution of the program :> :> ... :> :> Calder and :> Grunwald describe Measurement 2 as an upper bound on the number of :> dynamic calls that could be turned into static calls by powerful :> interprocedural optimizations, and Measurement 1 as the lower bound we :> would expect of such optimizations, since that much improvement can be :> achieved even by a naive interprocedural optimization. The mean values, :> over the programs they measured, were 31.6% for Measurement 1 and 66.3% :> for Measurement 2, a far cry from your Booch's 80%. To get a real idea of (theoretical) relative performance of dynamic binding compared to static binding, you need to know: 1) What is the relative performance of indirect calls as a fraction of the performance of direct calls. Are they 50% as efficient?, 40%?, 60%?, 70%?. 2) Are the above statistics (for C++?) the same for Eiffel and Ada? If we assume the statistics hold for Eiffel and Ada (which I wouldn't care to), the average relative performance of an optimised dynamic call compared with a statically bound call might be given by: %P_db_to_sb = ( (%_opt * 1) + ( (100 - %_opt) * %P_ind) ) / 100 where - %_opt is the percentage of dynamic calls that are optimised into static calls - %P_ind is the percentage relative performance of an indirect call compared to a direct call. This gives the following table of values for %P_db_to_sb: %_opt | 32 | 40 | 50 | 60 | 66 %P_ind | ---------------------------------------------------------------------- 40 | 59 | 64 | 70 | 76 | 79 50 | 66 | 70 | 75 | 80 | 83 60 | 73 | 76 | 80 | 84 | 86 70 | 80 | 82 | 85 | 88 | 90 80 | 86 | 88 | 90 | 92 | 93 Has anyone got any reliable statistics on %P_ind? Are any Eiifel vendors willing to offer values for %_opt? Note that this IS NOT an indication of relative performance of Eiffel to Ada. The statistics would apply equally well to both languages for equivalent code. Also, performance compared with complete static binding would be better than this because it excludes frozen declarations. ----- Hardware is out of my depth but what I'm wondering is whether the hardware could optimise by determining the indirection ahead of time so that the indirect call is effectively reduced to a direct one. If this were possible for 100% of calls (which I doubt), there would be no performance penalty for dynamic binding. Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison @ 1996-04-04 0:00 ` Dominique Colnet 1996-04-04 0:00 ` Steve Tynor ` (3 subsequent siblings) 4 siblings, 0 replies; 218+ messages in thread From: Dominique Colnet @ 1996-04-04 0:00 UTC (permalink / raw) |> Also, performance compared with complete static binding would be better than |> this because it excludes frozen declarations. SmallEiffel as a type inference mechanism. About 84 % of calls are statically bind. With SmallEiffel, code is as fast as C code :-) -- ------------------------------------------------------------------------------ |\/\/\/| : Dominique COLNET | | C.R.I.N. (Centre de Recherche en Informatique de Nancy) | (o)(o) : C _) : POST: CRIN,BP 239,54506 Vandoeuvre les Nancy Cedex,FRANCE | ,___| : | / : EMAIL: colnet@loria.fr VOICE : 83593079 /____\ FAX : 83413079 ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Dominique Colnet @ 1996-04-04 0:00 ` Steve Tynor 1996-04-08 0:00 ` Norman H. Cohen 1996-04-08 0:00 ` Matt Kennel ` (2 subsequent siblings) 4 siblings, 1 reply; 218+ messages in thread From: Steve Tynor @ 1996-04-04 0:00 UTC (permalink / raw) In article <DpBw9w.Mru@assip.csasyd.oz> donh@syd.csa.com.au (Don Harrison) writes: | where | - %_opt is the percentage of dynamic calls that are optimised into | static calls .. | Are any Eiifel vendors willing to offer values for %_opt? This, of course, is highly dependent on the particular system being compiled. The TowerEiffel optimizer prints this value out during its optimization phase. (actually it prints the percentage of calls that remain dynamic - so it's the inverse of your %_opt). I typically see values in the 10-20% range. That means 80-90% of calls are optimized to static calls (and without requiring the programmer to cripple the reusability of his classes by "freezing" their features). As you suggest, it's a valuable optimization technique. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Eiffel: Accept no substitutes. Steve Tynor Internet: Steve.Tynor@atlanta.twr.com Tower Technology WWW: http://www.twr.com/ ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Steve Tynor @ 1996-04-08 0:00 ` Norman H. Cohen 1996-04-09 0:00 ` Matt Kennel 0 siblings, 1 reply; 218+ messages in thread From: Norman H. Cohen @ 1996-04-08 0:00 UTC (permalink / raw) In article <TYNOR.96Apr4110411@twratl.atlanta.twr.com>, tynor@atlanta.twr.com (Steve Tynor) writes: I typically see |> values in the 10-20% range. That means 80-90% of calls are optimized |> to static calls In evaluating such statistics it is important to remember that in Eiffel ALL calls on nonfrozen features are nominally dispatching calls, even the ones that have an obvious target. In other words, in computing the percentage of calls to which this optimzation applies, Steve Tynor is not just considering calls (it's not clear whether we're counting run-time executions of calls or call sites here) that would nominally have been dispatching calls in Ada and counting the fraction of them that can be optimized into nondispatching calls. He's also taking all the calls that would have been written as nondispatching calls in Ada (or C++) in the first place, and including that number to both the numerator and denominator. It is not surprising that this gives a fraction closer to one than the fraction that was observed in Calder and Grunwald's C++ measurements. |> (and without requiring the programmer to cripple the |> reusability of his classes by "freezing" their features). The Ada approach, of course, is not to freeze features, but to allow certain variables and function results to be declared as belonging to a known DYNAMIC type. The same (unfrozen) subprogram that is invoked as a dispatching subprogram at call sites where the actual parameter is of unknown dynamic type is invoked as a nondispatching subprogram at call sites where the actual parameter is of a known dynamic type. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-08 0:00 ` Norman H. Cohen @ 1996-04-09 0:00 ` Matt Kennel 0 siblings, 0 replies; 218+ messages in thread From: Matt Kennel @ 1996-04-09 0:00 UTC (permalink / raw) Norman H. Cohen (ncohen@watson.ibm.com) wrote: : In article <TYNOR.96Apr4110411@twratl.atlanta.twr.com>, : tynor@atlanta.twr.com (Steve Tynor) writes: : I typically see : |> values in the 10-20% range. That means 80-90% of calls are optimized : |> to static calls : In evaluating such statistics it is important to remember that in Eiffel : ALL calls on nonfrozen features are nominally dispatching calls, even the : ones that have an obvious target. In other words, in computing the : percentage of calls to which this optimzation applies, Steve Tynor is not : just considering calls (it's not clear whether we're counting run-time : executions of calls or call sites here) that would nominally have been : dispatching calls in Ada and counting the fraction of them that can be : optimized into nondispatching calls. He's also taking all the calls that : would have been written as nondispatching calls in Ada (or C++) in the : first place, and including that number to both the numerator and : denominator. It is not surprising that this gives a fraction closer to : one than the fraction that was observed in Calder and Grunwald's C++ : measurements. I just recompiled my 'chaotic data analysis' program in Sather, which distinguishes concrete classes from abstract classes. The compiler reported 109 abstract calls and 4309 concrete calls. That's 2.5%. Of course, in Eiffel, if you do not inherit from a class then calls to such class features can be treated as concrete without further data flow analysis. : -- : Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Dominique Colnet 1996-04-04 0:00 ` Steve Tynor @ 1996-04-08 0:00 ` Matt Kennel 1996-04-09 0:00 ` Norman H. Cohen 1996-04-09 0:00 ` Robert C. Martin 1996-04-10 0:00 ` J. Kanze 4 siblings, 1 reply; 218+ messages in thread From: Matt Kennel @ 1996-04-08 0:00 UTC (permalink / raw) Don Harrison (donh@syd.csa.com.au) wrote: : To get a real idea of (theoretical) relative performance of dynamic binding : compared to static binding, you need to know: : 1) What is the relative performance of indirect calls as a fraction of the : performance of direct calls. Are they 50% as efficient?, 40%?, 60%?, 70%?. : 2) Are the above statistics (for C++?) the same for Eiffel and Ada? My experience with Sather numerical classes is that the cost of indirect calls is not the actual indirection, but the lost opportunities for inlining. If you can do enough repeated inlining (i.e. inlining of routines exposes new concretized calls which are further inlined and substantial constant and data propagation) you can get very large speed improvements in some circumstances. Matrix classes are the canonical example: any remaining indirection anywhere in the chain can completely blow performance compared to the standard algorithm. I believe the Sather compiler itself has fewer than 10% of its call points dispatched. (this is a static, not dynamic count). Among all compilation options, turning inlining on and off has the greatest observed effect on resulting execution speed. : ----- : Hardware is out of my depth but what I'm wondering is whether the hardware could : optimise by determining the indirection ahead of time so that the indirect call : is effectively reduced to a direct one. If this were possible for 100% of calls : (which I doubt), there would be no performance penalty for dynamic binding. No I don't believe so, you really want to have a real compiler do its optimization tricks to get best use of static calls. : Don. cheers Matt ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-08 0:00 ` Matt Kennel @ 1996-04-09 0:00 ` Norman H. Cohen 0 siblings, 0 replies; 218+ messages in thread From: Norman H. Cohen @ 1996-04-09 0:00 UTC (permalink / raw) In article <4kbl7r$1i8@gaia.ns.utk.edu>, mbk@caffeine.engr.utk.edu (Matt Kennel) writes: |> My experience with Sather numerical classes is that the cost of indirect |> calls is not the actual indirection, but the lost opportunities for |> inlining. Plus the opportunities that arise from cross-procedural analysis, for compilers that perform such analysis for statically bound subprograms. (But remember, in counting the cost of the actual indirection, to count machine cycles rather than instruction path length. Indirect function calls can cause expensive I-cache misses on some architectures.) -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison ` (2 preceding siblings ...) 1996-04-08 0:00 ` Matt Kennel @ 1996-04-09 0:00 ` Robert C. Martin 1996-04-10 0:00 ` J. Kanze 4 siblings, 0 replies; 218+ messages in thread From: Robert C. Martin @ 1996-04-09 0:00 UTC (permalink / raw) Don Harrison (donh@syd.csa.com.au) wrote: : To get a real idea of (theoretical) relative performance of : dynamic binding compared to static binding, you need to know: : 1) What is the relative performance of indirect calls as a : fraction of the performance of direct calls. Are they 50% as : efficient?, 40%?, 60%?, 70%?. I recently conducted just such an experiment in C++. class F { public: void nv(); virtual void v(); }; void F::nv() {} void F::v() {} F f; F& fr=f; fr.nv(); fr.v(); Executing this on a 486-33 laptop I found that the call to nv took 998ns and the call to v took 1142ns, or 144 extra nanoseconds. The compiler was Borland 4.1. On the other hand, I benchmarked this same test on a popular compiler for the 68000. The machine was proprietary, and so I cannot give you the exact environment. However the timings were telling. nv required about 2000ns, and v required 4000ns. Nearly a doubling. It seems clear that the performance delta between virtual and non-virtual functions can vary significantly between compilers. -- Robert Martin | Design Consulting | Training courses offered: Object Mentor Assoc.| rmartin@oma.com | OOA/D, C++, Advanced OO 14619 N. Somerset Cr| Tel: (847) 918-1004 | Mgt. Overview of OOT Green Oaks IL 60048 | Fax: (847) 918-1023 | http://www.oma.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO 1996-04-04 0:00 ` Don Harrison ` (3 preceding siblings ...) 1996-04-09 0:00 ` Robert C. Martin @ 1996-04-10 0:00 ` J. Kanze 4 siblings, 0 replies; 218+ messages in thread From: J. Kanze @ 1996-04-10 0:00 UTC (permalink / raw) In article <RMARTIN.96Apr9155433@rcm.oma.com> rmartin@oma.com (Robert C. Martin) writes: |> I recently conducted just such an experiment in C++. |> class F |> { |> public: |> void nv(); |> virtual void v(); |> }; |> void F::nv() {} |> void F::v() {} |> F f; |> F& fr=f; |> fr.nv(); |> fr.v(); |> Executing this on a 486-33 laptop I found that the call to nv took |> 998ns and the call to v took 1142ns, or 144 extra nanoseconds. The |> compiler was Borland 4.1. |> On the other hand, I benchmarked this same test on a popular compiler |> for the 68000. The machine was proprietary, and so I cannot give you |> the exact environment. However the timings were telling. nv required |> about 2000ns, and v required 4000ns. Nearly a doubling. |> It seems clear that the performance delta between virtual and |> non-virtual functions can vary significantly between compilers. At least for empty functions called from a tight loop:-). Independantly of that actual call/return sequence, a function call involves two things: finding the address of the function, and setting up the local stack frame. The only difference between virtual and non-virtual functions will be in the first, finding the address. If the function is empty, or simply if it has no local variables or arguments, some compilers will optimize by not building the local stack frame. Since I suspect that on an 80x86 architecture, building the local stack frame costs more than the actual call, the percentage gain relative to the actual call will be much more significant if the compiler does this optimization. (From the Borland figures, I suspect that it doesn't.) On the other hand, the actual function address is in fact a loop invariant; a compiler could potentially hoist it out of the loop completely. In this case, we might actually measure that virtual functions were faster. Even holding `fr' in a register in the loop will make a noticeable gain (and will reduce the difference between virtual and non-virtual functions). I would suggest that neither of these conditions are typical in real programs. Depending on what the compiler optimizes, they may be slanted for or against virtual functions, but either way, they do not exterpolate easily. The only practical solution is to write your program cleanly, and profile if there is a performance problem. If profiling shows that an inordinate amount of time is spent in a small loop which does nothing but call a trivial virtual function, and in fact, the function involved is always the same, I would consider making the function non-virtual. Otherwise, I wouldn't bother. (I would probably also look at the generated assembler. If the compiler is already hoisting the calcule of the virtual address out of the loop, there's not much to be gained by making the function non-virtual.) -- James Kanze (+33) 88 14 49 00 email: kanze@gabi-soft.fr GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France Conseils en informatique industrielle -- -- Beratung in industrieller Datenverarbeitung ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO @ 1996-05-02 0:00 Bob Crispen 0 siblings, 0 replies; 218+ messages in thread From: Bob Crispen @ 1996-05-02 0:00 UTC (permalink / raw) Richard Riehle sez: >Robert Dewar (dewar@cs.nyu.edu) wrote: [snip] >: P.S. spaghetti code doesn't just mean code you don't like. It is a rather >: specific term used to described the non-nested chaotic control structures >: caused by undisciplined use of gotos or equivalent transfers of control. >: I think it is useful to keep this sense. > > HmmmMMMMMMmmmmm. I guess we could characterize OOP as "ravioli code" > rather than spaghetti code. Each class is a little square of pasta > enclosing the encapsulated delicacies. And often with about as much internal structure as a plate of ravioli. Good metaphor. > Anyone for Pizza code? Dump a side order of spaghetti on top of a pepperoni pizza and you get a perfect representation of what some new kids draw when I ask them, "Draw me a picture of your main system elements and the dataflows between them." -- shortly before they pick up the paper and say, "Uh, let me get back to you on this." Don't forget pasta fazool code, where the subprograms and processes intercommunicate through a bowl of variable soup (shared memory). Preferable to all of these is spumoni code -- or at least the kind of spumoni you can get in Alabama (often spoken of as "little Italy" by people who are severely geographically impaired) where the ice cream looks exactly like those diagrams of Unix. Bob "Vito" Crispen revbob@eight-ball.hv.boeing.com Speaking for myself, not my company ^ permalink raw reply [flat|nested] 218+ messages in thread
[parent not found: <DoDLr7.JDB@world.std.com>]
[parent not found: <4if7s5$bfk@ra.nrl.navy.mil>]
[parent not found: <DoDqH4.29v@world.std.com>]
* Re: Real OO [not found] ` <DoDqH4.29v@world.std.com> @ 1996-03-26 0:00 ` AdaWorks 0 siblings, 0 replies; 218+ messages in thread From: AdaWorks @ 1996-03-26 0:00 UTC (permalink / raw) Robert A Duff (bobduff@world.std.com) wrote: : In article <4if7s5$bfk@ra.nrl.navy.mil>, : Richard Pitre <pitre@n5160d.nrl.navy.mil> wrote: : >I figured that much. But how it this a limitation? : It's a limitation by comparison with a "pure" OO language, in which : every built-in data type, including things like integers, is a class, : and you can do all the OOP-ish things with them. I didn't say this : limitation is important. This thread started as a discussion of various : differences between Ada and Eiffel, and this is one such difference. Of course there is no inherent limitation in Ada. If one wanted to be absolutely true to the tenets of OOP, one could do something like this: package Number is type Base_Number is abstract tagged private; function "+" (L, R : Base_Number) return Base_Number is abstract; -- also operators for "-", "*", ... etc. private type Base_Number is abstract tagged null record; end Number; Then create concrete number packages for each kind of number. For example, private type Int_16 is range -2**16 .. 2**16 -1; type Integer_16 is new Base_Number with record Value : Int_16; end record; or later, private type R_16 is new Float range ...; type Real16 is new Base_Number with record Value : R_16; end record; Before you exclaim, "Arghhhhhhh" let me suggest that there might be circumstances in which you really want to create your own very restricted variation of the arithmetic operators for the concrete types derived from this abstract class. In fact, this could be the foundation abstract class for the famous Fractions package, the Vector Arithmetic package, or several other packages that service non-standard number models. Furthermore, although we customarily use numbers defined in the Ada LRM, nothing prevents us from approaching number definitions in a more "pure" OOP mode. I realize, such an approach will be rare, but I can imagine safety-sensitive applications where it might make sense to do something such as this. Richard Riehle adaworks@netcom.com -- richard@adaworks.com AdaWorks Software Engineering Suite 27 2555 Park Boulevard Palo Alto, CA 94306 (415) 328-1815 FAX 328-1112 ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO [not found] ` <4if7s5$bfk@ra.nrl.navy.mil> [not found] ` <DoDqH4.29v@world.std.com> @ 1996-03-29 0:00 ` Brian Rogoff 1 sibling, 0 replies; 218+ messages in thread From: Brian Rogoff @ 1996-03-29 0:00 UTC (permalink / raw) Of course there is no inherent limitation in Ada. If one wanted to be absolutely true to the tenets of OOP, one could do something like this: package Number is type Base_Number is abstract tagged private; function "+" (L, R : Base_Number) return Base_Number is abstract; -- also operators for "-", "*", ... etc. private type Base_Number is abstract tagged null record; end Number; <rest of example deleted for space> Before you exclaim, "Arghhhhhhh" let me suggest that there might be circumstances in which you really want to create your own very restricted variation of the arithmetic operators for the concrete types derived from this abstract class. In fact, this could be the foundation abstract class for the famous Fractions package, the Vector Arithmetic package, or several other packages that service non-standard number models. Furthermore, although we customarily use numbers defined in the Ada LRM, nothing prevents us from approaching number definitions in a more "pure" OOP mode. I realize, such an approach will be rare, but I can imagine safety-sensitive applications where it might make sense to do something such as this. Richard Riehle adaworks@netcom.com I think that the main concern that I would have with such an approach, i.e. representing numbers as tagged types, is the overhead of having a tag associated with each numeric item. If you define your "Rational" type this way, and then start manipulating Rational matrices, can you be reasonably sure that your Ada compiler will eliminate the tags if your Rational is not involved in runtime dispatching? Or will you get an M x N x sizeof(tag) overhead for every M x N RationalMatrix? I think a better approach might be to use generic formal package parameters to create type signatures for the numeric types you create. It is a little syntactically heavy though, and I kind of wish there were a nicer way to do this sort of thing in Ada... -- Brian ^ permalink raw reply [flat|nested] 218+ messages in thread
[parent not found: <JSA.96Mar13143956@organon.com>]
* Re: Real OO [not found] <JSA.96Mar13143956@organon.com> @ 1996-03-15 0:00 ` Don Harrison 0 siblings, 0 replies; 218+ messages in thread From: Don Harrison @ 1996-03-15 0:00 UTC (permalink / raw) Jon S Anthony writes: :In article <4i455s$ijq@watnews1.watson.ibm.com> ncohen@watson.ibm.com (Norman H. Cohen) writes: : :> In article <Do3F1K.4xG@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: :> :> | > Notice what is happening here. Now that Person_Type is distinct, it :> | > now becomes more reusable than it was when it was :> | > co-encapsulated. By generalising, you have have increased :> | > reusability. If you take the process a step further and separate :> | > Pairing_Type into a distinct abstraction (because it relates to :> | > other domains than family trees), you increase reusability :> | > further. If you keep on going, you end up with a pure OO model of :> | > the problem domain in which reusability has been maximised. :> | > Although the software still does the same job, it is much more :> | > reusable with this structure. :> ... :> One may write many different applications all of which have a class named :> Person, but these classes may have little or nothing in common. Even if :> they all share a Name attribute, it may be more sensible to define a new :> class from scratch each time than to burden each application with a :> Person class general enough to be shared by all of them. Reusability :> pays off for large chunks of software, but the payoff does not scale down :> to microscopic fragments. : :The importance of this observation really cannot be overemphasized. :It is well known, researched and discussed in the reuse literature. Can you suggest some specific papers/articles which convey this position? :Trying to build the one "jumbo general purpose" abstraction for a :particular concept is doomed quite simply because no such thing :exists. About the most you can do is define suites of related :abstractions exhibiting various aspects of commonality and variability :that "you" are interested in with respect to the various related :domains. It is also worth stating that this is _not_ a bad thing. It :may not be a "good" thing either (depending on your temperment). If :you make lots of observations and build various models of the process :and resulting abstraction definitions this fact starts to become :_very_ clear. The fact that the real world is a complex place with many interactions gives credence to this argument. As you suggest, it is only feasible to model the subsets of it we are interested in. I'm unconvinced, however, that a pure OO model is inappropriate for this task. What do pure OO developers have to say about this? - Eiffelers, Smalltalkers? :/Jon :-- :Jon Anthony :Organon Motives, Inc. :1 Williston Road, Suite 4 :Belmont, MA 02178 : :617.484.3383 :jsa@organon.com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
[parent not found: <4hneps$1238@watnews1.watson.ibm.com>]
[parent not found: <Do3F1K.4xG@assip.csasyd.oz>]
[parent not found: <4i455s$ijq@watnews1.watson.ibm.com>]
* Re: Real OO [not found] ` <4i455s$ijq@watnews1.watson.ibm.com> @ 1996-03-15 0:00 ` Don Harrison [not found] ` <DoBH80.9u9@world.std.com> 1996-03-16 0:00 ` Des Kenny 1 sibling, 1 reply; 218+ messages in thread From: Don Harrison @ 1996-03-15 0:00 UTC (permalink / raw) Norman H. Cohen) writes: :In article <Do3F1K.4xG@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: : :|> Notice what is happening here. Now that Person_Type is distinct, it now becomes :|> more reusable than it was when it was co-encapsulated. By generalising, you have :|> have increased reusability. If you take the process a step further and separate :|> Pairing_Type into a distinct abstraction (because it relates to other domains than :|> family trees), you increase reusability further. If you keep on going, you end up :|> with a pure OO model of the problem domain in which reusability has been maximised. :|> Although the software still does the same job, it is much more reusable with this :|> structure. : :I wish I could remember the author of this quote: "Every problem is a :special case of some more general problem, and we usually discover this :fact much too soon for our own good." : :One may write many different applications all of which have a class named :Person, but these classes may have little or nothing in common. Even if :they all share a Name attribute, it may be more sensible to define a new :class from scratch each time than to burden each application with a :Person class general enough to be shared by all of them. Reusability :pays off for large chunks of software, but the payoff does not scale down :to microscopic fragments. Not so sure about that. A good example is that of the basic types of INTEGER and REAL. In Eiffel, these are explicitly defined as classes offering operations such as '+', '-', '*', '/' (they actually inherit these from other classes) which may be inherited by subtypes such as VELOCITY, DISTANCE etc. which might apply range and other constraints. Clearly, the heavy reuse of these 'micro' classes carries the benefit of their definition at the micro level through to all higher levels. Although such basic types are not explicitly defined as classes in Ada, they are effectively classes because their features are 'inherited' through subtyping and derivation. You wouldn't want to define '+' for every arithmetic abstraction. Maybe, the potential for reuse should be the determining factor. Perhaps, if it is plain that a component of an abstraction will never be needed in it's own right (how can you know), then it may be acceptable not to separate it. However, the potential for sloppy design is obvious - developers would tend to take the expedient path of rarely or never anticipating long term reuse. :I am fully sympathetic with trying to anticipate other uses for the :software components we are building, and trying to make them general :enough to be reused, but generality usually comes at the expense of :efficiency and simplicity, so it can be carried too far. Sometimes there :is a specific problem, such as the recording of a family tree, to be :solved, and the most economical choice is just to go ahead and solve the :problem at hand rather than a broader class of problems that may never :arise. Certainly, in the short term. :True, ignoring reuse can lead to wasteful duplication of effort and more :diffcult and expensive maintenance, but blindly repeating the mantra of :reusability can lead to absurd choices. I firmly believe (but have no concrete evidence to back it up) that development of software characterised by a consistent focus on reuse (by inheritance or composition), will save time within the life of a project and deliver a better quality product. Regretably, many managers fail to recognise this fact and focus too much on short term expediency to the detriment of their staff and clients. :-- :Norman H. Cohen ncohen@watson.ibm.com Don. ^ permalink raw reply [flat|nested] 218+ messages in thread
[parent not found: <DoBH80.9u9@world.std.com>]
* Re: Real OO [not found] ` <DoBH80.9u9@world.std.com> @ 1996-03-15 0:00 ` Mark A Biggar 1996-03-15 0:00 ` Richard Pitre 1 sibling, 0 replies; 218+ messages in thread From: Mark A Biggar @ 1996-03-15 0:00 UTC (permalink / raw) In article <DoBH80.9u9@world.std.com> bobduff@world.std.com (Robert A Duff) writes: >In article <DoArzG.7v1@assip.csasyd.oz>, >Don Harrison <donh@syd.csa.com.au> wrote: >>Although such basic types are not explicitly defined as classes in Ada, >>they are effectively classes because their features are 'inherited' >>through subtyping and derivation. You wouldn't want to define '+' for >>every arithmetic abstraction. >Just to clarify, Ada *does* use the term "class" to refer to the set of >all integer types, for example. And you are correct that you get >inheritance for these classes -- both for predefined things like '+' and >for user-defined operations. However, you do not get type extension >(you can't add new fields to an integer type when you derive from it), >and you do not get polymorphism. And in early Ada9X drafts there was the idea of being able to write routines that took parameters like function foo(X: INTEGER'CLASS) return INTEGER; which would allow you to pass in an argument of any type derived from INTEGER, but given the type view casting rules, that didn't give you any thing that just using a conversion to INTEGER on a routine with an INTEGER formal already does and added extra complexity to the language as well, so it was droped. -- Mark Biggar mab@wdl.loral.com ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO [not found] ` <DoBH80.9u9@world.std.com> 1996-03-15 0:00 ` Mark A Biggar @ 1996-03-15 0:00 ` Richard Pitre 1 sibling, 0 replies; 218+ messages in thread From: Richard Pitre @ 1996-03-15 0:00 UTC (permalink / raw) In article <DoBH80.9u9@world.std.com> bobduff@world.std.com (Robert A Duff) writes: > In article <DoArzG.7v1@assip.csasyd.oz>, > Don Harrison <donh@syd.csa.com.au> wrote: > >Although such basic types are not explicitly defined as classes in Ada, > >they are effectively classes because their features are 'inherited' > >through subtyping and derivation. You wouldn't want to define '+' for > >every arithmetic abstraction. > > Just to clarify, Ada *does* use the term "class" to refer to the set of > all integer types, for example. And you are correct that you get > inheritance for these classes -- both for predefined things like '+' and > for user-defined operations. However, you do not get type extension > (you can't add new fields to an integer type when you derive from it), > and you do not get polymorphism. > > - Bob I'm just learning Ada and I don't understand what you mean by "can't add new fields ...". Does this mean that when you subclass a built in integer type that you can only add new methods but no new instance variables? richard ^ permalink raw reply [flat|nested] 218+ messages in thread
* Re: Real OO [not found] ` <4i455s$ijq@watnews1.watson.ibm.com> 1996-03-15 0:00 ` Don Harrison @ 1996-03-16 0:00 ` Des Kenny 1 sibling, 0 replies; 218+ messages in thread From: Des Kenny @ 1996-03-16 0:00 UTC (permalink / raw) In article <4i455s$ijq@watnews1.watson.ibm.com>, ncohen@watson.ibm.com wrote: > In article <Do3F1K.4xG@assip.csasyd.oz>, donh@syd.csa.com.au (Don Harrison) writes: > > |> Notice what is happening here. Now that Person_Type is distinct, it now becomes > |> more reusable than it was when it was co-encapsulated. By generalising, you have > |> have increased reusability. If you take the process a step further and separate > |> Pairing_Type into a distinct abstraction (because it relates to other domains than > |> family trees), you increase reusability further. If you keep on going, you end up > |> with a pure OO model of the problem domain in which reusability has been maximised. > |> Although the software still does the same job, it is much more reusable with this > |> structure. > > I wish I could remember the author of this quote: "Every problem is a > special case of some more general problem, and we usually discover this > fact much too soon for our own good." > > One may write many different applications all of which have a class named > Person, but these classes may have little or nothing in common. Even if > they all share a Name attribute, it may be more sensible to define a new > class from scratch each time than to burden each application with a > Person class general enough to be shared by all of them. Reusability > pays off for large chunks of software, but the payoff does not scale down > to microscopic fragments. > > I am fully sympathetic with trying to anticipate other uses for the > software components we are building, and trying to make them general > enough to be reused, but generality usually comes at the expense of > efficiency and simplicity, so it can be carried too far. Sometimes there > is a specific problem, such as the recording of a family tree, to be > solved, and the most economical choice is just to go ahead and solve the > problem at hand rather than a broader class of problems that may never > arise. > > True, ignoring reuse can lead to wasteful duplication of effort and more > diffcult and expensive maintenance, but blindly repeating the mantra of > reusability can lead to absurd choices. > > -- > Norman H. Cohen ncohen@watson.ibm.com Large scale reuse benefits from both tools and organisational roles. Customer product development project team(s) are focused on product project milestones, product quality and cost; and the class library architecture team(s) who are focused on longer term benefits and large scale investment. These teams need to work together to achieve the short term objectives of producing a quality product on time and budget and the longer term objective of continuously improving the process of future product development. Developing any item for a variety of product uses is not an easy design task and that is why the class library architecture team have different skills and a longer term focus than the customer product team. These kinds of separations of roles are not unique to software production. In hardware manufacturing engineering there are fitters and turners that use lathes to make a wide range of customer products; then there are other mechanical engineers who design the lathes so that they may be used for product development. In the music area there are violin players that play an almost limitless range of music and then there are the people that design the violins so that the music can be played. Throughout history these types of roles have existed, there have always been tool users and tool makers. Class libraries that have a long life are best built by the tool makers who are generalists and customer products are best built by the tool users who are trying to satisfy a specific customer need. The boundary is not always sharp and it changes over time with experience. There will always be design iterations of the product development and the process used to make the product. Many of the most common constructs are already available in the Eiffel class libraries. These are tools that are readily available. If I am working on a customer product development I first look at the class libraries to see what tools are available for the task at hand. If I can find a suitable set of classes I will consider them first. It may be that they are not all suitable and some extensions and redefinitions are required; which is fine. The experience of this project is then fed back to the class library architects and they consider what , if anything, should be done to the library architecture for future projects. They are focused on the longer term investment and continuous improvement of the production process. So to my way of thinking the work for the classs Family_Tree is almost all done for me; because I have done my homework and studied the Eiffel class libraries carefully. I know, immediately, that the Collection cluster has a number of tree classes which are worth considering. So I can at once get a preliminary sketch of the design: Class Family_Tree[G] inherit Tree[T] ..... ..... end --Family_Tree And depending on the maturity of the class library and the skill of its designers I have a reasonable chance that 80% of my work is done already. Of course, 20%, or so, of the time I have to do more work because there are special circumstances that are not so easily taken care of by the past experience of the class architects. It's not perfect but its pretty good 80% of the time - and that's not a bad thing. It leaves you more time to focus on the harder/novel parts because the well defined parts are done already. This is also nothing new, most other professions : Mathematicians, Scientists, Engineers, Lawyers ... behave like this all the time. Maybe it is an accident of history related to the ease with which software developers can generate millions of lines of source code text at unprecedented speed that they have this illusion that they are always creating something new. They do not need to be economic in generating source text because they seem to have unlimited machine resources at their disposal. Compare this to any other profession in history , before text processors, they had to be economic and reuse what was already done by others because there was simply not enough time to recreate everthing from scratch all over again. So they developed classification systems for Periodic Tables of the elements, for Legal statutes, for Mathematical Algebras, for Physical laws, Engineering designs. No Mathemetician or Lawyer would dream of reinventing anything, unless they had not done their homework! It is just not economic,there isn't time to do it all over again every day! Maybe the text editor and the word processor are our undoing, because we can reinvent the basic structures of almost every application and computer science construct all over again every day! Software developers have just figured out a way to be uneconomic much faster than anyone else. Like in Alice in Wonderland they have found out how to make the world go as fast as they do so they can run faster to stay in the same place. Pretty clever eh! Maybe we need to go back to contemplation of the works of others, as people were compelled to do before text processors, and learn to write less source text not more. And it's good night from him! Cheers Des Kenny Objective Methods Ltd Email: dkenny@actrix.gen.nz ^ permalink raw reply [flat|nested] 218+ messages in thread
end of thread, other threads:[~1996-05-22 0:00 UTC | newest] Thread overview: 218+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <4id031$cf9@dayuc.dayton.saic.com> 1996-03-18 0:00 ` Real OO Norman H. Cohen 1996-03-18 0:00 ` The Right Reverend Colin James III 1996-03-19 0:00 ` Norman H. Cohen 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name The Right Reverend Colin James III 1996-03-20 0:00 ` Dave Retherford 1996-03-20 0:00 ` Real OO Dale Stanbrough 1996-03-21 0:00 ` Richard Pitre 1996-03-20 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) David Emery 1996-03-20 0:00 ` Mark R Okern - F95 1996-03-20 0:00 ` Real OO John G. Volan 1996-03-21 0:00 ` Scott Leschke 1996-03-21 0:00 ` Norman H. Cohen 1996-03-21 0:00 ` Robert A Duff 1996-03-22 0:00 ` Don Harrison 1996-03-21 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) Richard A. O'Keefe 1996-03-21 0:00 ` Robert Dewar 1996-03-20 0:00 ` Norman Cohen giving IBM a bad name Brian & Karen Bell 1996-03-21 0:00 ` Kent Mitchell 1996-03-22 0:00 ` Robert Munck 1996-03-22 0:00 ` Mark Brennan 1996-03-22 0:00 ` David Curry 1996-03-23 0:00 ` Tom Reid 1996-03-21 0:00 ` Real OO Don Harrison 1996-03-21 0:00 ` Colin James III giving humans a bad name (was Re: Norman Cohen giving IBM a bad name) Ulrich Windl 1996-03-20 0:00 ` Real OO Don Harrison 1996-03-22 0:00 ` Don Harrison 1996-03-22 0:00 ` Norman H. Cohen 1996-03-26 0:00 ` Don Harrison 1996-03-26 0:00 ` Norman H. Cohen 1996-03-29 0:00 ` Don Harrison 1996-03-27 0:00 ` Thomas Beale 1996-03-28 0:00 ` Don Harrison 1996-03-22 0:00 ` Norman H. Cohen 1996-03-27 0:00 ` Don Harrison 1996-03-27 0:00 ` Norman H. Cohen 1996-03-28 0:00 ` Jacob Gore 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Tucker Taft 1996-04-04 0:00 ` Tucker Taft 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Don Harrison 1996-04-15 0:00 ` Robert I. Eachus 1996-04-19 0:00 ` Don Harrison 1996-04-19 0:00 ` Matt Kennel 1996-04-20 0:00 ` Bob Hathaway 1996-04-23 0:00 ` Don Harrison 1996-04-04 0:00 ` Jon S Anthony 1996-04-04 0:00 ` Laurent Guerby 1996-04-04 0:00 ` Robb Nebbe 1996-03-23 0:00 ` Joachim Durchholz 1996-03-26 0:00 ` Norman H. Cohen 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Jon S Anthony 1996-04-12 0:00 ` Don Harrison 1996-04-17 0:00 ` Jon S Anthony 1996-04-19 0:00 ` Don Harrison 1996-04-19 0:00 ` Jon S Anthony 1996-04-23 0:00 ` Don Harrison 1996-04-24 0:00 ` Don Harrison 1996-04-29 0:00 ` Jon S Anthony 1996-04-30 0:00 ` Robert Dewar 1996-04-30 0:00 ` Amit Patel 1996-04-30 0:00 ` Robert A Duff 1996-05-07 0:00 ` Amit Patel 1996-05-01 0:00 ` Norman H. Cohen 1996-05-01 0:00 ` Colin James III (The Rt Rev'd) 1996-05-07 0:00 ` Amit Patel 1996-04-30 0:00 ` Robert A Duff 1996-04-30 0:00 ` Amit Patel 1996-04-30 0:00 ` Robert Dewar 1996-05-01 0:00 ` Richard Bielak 1996-05-01 0:00 ` Adam Beneschan 1996-05-02 0:00 ` Ell 1996-05-01 0:00 ` AdaWorks 1996-05-01 0:00 ` Don Harrison 1996-05-01 0:00 ` David Hopwood 1996-05-03 0:00 ` Don Harrison 1996-05-01 0:00 ` Don Harrison 1996-05-02 0:00 ` Robert A Duff 1996-05-03 0:00 ` Don Harrison 1996-05-03 0:00 ` Robert A Duff 1996-05-06 0:00 ` Don Harrison 1996-05-06 0:00 ` Robb Nebbe 1996-05-06 0:00 ` Robert A Duff 1996-05-02 0:00 ` Robert A Duff 1996-05-03 0:00 ` Don Harrison 1996-05-10 0:00 ` Don Harrison 1996-05-08 0:00 ` Joachim Durchholz 1996-05-03 0:00 ` Don Harrison 1996-05-03 0:00 ` Dave Fitch 1996-05-07 0:00 ` Jon S Anthony 1996-04-30 0:00 ` Joachim Durchholz 1996-04-30 0:00 ` Jon S Anthony 1996-05-01 0:00 ` Matt Kennel 1996-05-03 0:00 ` Don Harrison 1996-05-02 0:00 ` Don Harrison 1996-05-02 0:00 ` Robert I. Eachus 1996-05-02 0:00 ` Jon S Anthony 1996-05-03 0:00 ` Don Harrison 1996-05-06 0:00 ` Jon S Anthony 1996-05-06 0:00 ` Jon S Anthony 1996-05-06 0:00 ` Don Harrison 1996-05-06 0:00 ` Don Harrison 1996-05-07 0:00 ` Jon S Anthony 1996-05-13 0:00 ` Don Harrison 1996-05-09 0:00 ` Jon S Anthony 1996-04-19 0:00 ` Multiple Dispatch in Ada 95 (Was Re: Real OO) Brian Rogoff 1996-04-21 0:00 ` Brian Rogoff 1996-04-19 0:00 ` Robert I. Eachus 1996-04-20 0:00 ` Brian Rogoff 1996-04-21 0:00 ` Robert A Duff 1996-04-24 0:00 ` Real OO Joachim Durchholz 1996-05-01 0:00 ` Matt Kennel 1996-05-02 0:00 ` Don Harrison 1996-05-07 0:00 ` Joachim Durchholz 1996-05-08 0:00 ` Jon S Anthony 1996-05-09 0:00 ` Robert I. Eachus 1996-04-30 0:00 ` Jon S Anthony 1996-05-03 0:00 ` Don Harrison 1996-05-07 0:00 ` Jon S Anthony 1996-04-30 0:00 ` Joachim Durchholz 1996-05-08 0:00 ` Joachim Durchholz 1996-05-10 0:00 ` Jon S Anthony 1996-05-02 0:00 ` Jon S Anthony 1996-05-06 0:00 ` Jon S Anthony 1996-04-08 0:00 ` Norman H. Cohen 1996-04-08 0:00 ` Robert A Duff 1996-04-09 0:00 ` Norman H. Cohen 1996-04-10 0:00 ` Don Harrison 1996-04-11 0:00 ` Jacob Gore 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Jacob Gore 1996-04-16 0:00 ` Don Harrison 1996-04-12 0:00 ` Don Harrison 1996-04-12 0:00 ` Matt Kennel 1996-04-15 0:00 ` Don Harrison 1996-04-12 0:00 ` Jon S Anthony 1996-04-13 0:00 ` Robert A Duff 1996-04-16 0:00 ` Jon S Anthony 1996-04-09 0:00 ` Valery CROIZIER 1996-04-09 0:00 ` Jon S Anthony 1996-04-09 0:00 ` Joachim Durchholz 1996-05-02 0:00 ` Joachim Durchholz 1996-05-05 0:00 ` Robert A Duff 1996-05-05 0:00 ` Robert Dewar 1996-05-06 0:00 ` Norman H. Cohen 1996-05-07 0:00 ` Don Harrison 1996-05-07 0:00 ` Jon S Anthony 1996-05-08 0:00 ` Don Harrison 1996-05-08 0:00 ` Norman H. Cohen 1996-05-08 0:00 ` Robert A Duff 1996-05-10 0:00 ` Matt Kennel 1996-05-10 0:00 ` Robert A Duff 1996-05-14 0:00 ` Matt Kennel 1996-05-15 0:00 ` Robert A Duff 1996-05-07 0:00 ` Ada terminology (was Re: Real OO) David Hopwood 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-07 0:00 ` Dave Jones 1996-05-07 0:00 ` Tucker Taft 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-08 0:00 ` bill.williams 1996-05-07 0:00 ` Real OO Amit Patel 1996-05-07 0:00 ` The Right Reverend Colin James III 1996-05-08 0:00 ` Don Harrison 1996-05-08 0:00 ` Juergen Schlegelmilch [not found] ` <Dr4538.D27@assip.csasyd.oz> 1996-05-09 0:00 ` Juergen Schlegelmilch 1996-05-09 0:00 ` Richard Riehle 1996-05-10 0:00 ` Tucker Taft 1996-05-13 0:00 ` Don Harrison 1996-05-13 0:00 ` Tucker Taft 1996-05-14 0:00 ` Joachim Durchholz 1996-05-14 0:00 ` Roger Browne 1996-05-14 0:00 ` Don Harrison 1996-05-14 0:00 ` Robert A Duff 1996-05-14 0:00 ` Steve Tynor 1996-05-14 0:00 ` Robert A Duff 1996-05-15 0:00 ` Don Harrison 1996-05-15 0:00 ` Steve Tynor 1996-05-15 0:00 ` Robert A Duff 1996-05-16 0:00 ` James McKim 1996-05-18 0:00 ` Matt Kennel 1996-05-20 0:00 ` James McKim 1996-05-22 0:00 ` Matt Kennel 1996-05-15 0:00 ` Alexander Kjeldaas 1996-05-15 0:00 ` Steve Tynor 1996-05-19 0:00 ` Piercarlo Grandi 1996-05-14 0:00 ` James McKim 1996-05-15 0:00 ` Juergen Schlegelmilch 1996-05-20 0:00 ` Joachim Durchholz 1996-05-07 0:00 ` Joachim Durchholz 1996-05-09 0:00 ` Don Harrison 1996-05-09 0:00 ` Jon S Anthony 1996-05-09 0:00 ` Joachim Durchholz 1996-04-02 0:00 ` Detecting type mismatch at compile time (was Re: Real OO) Robert I. Eachus 1996-04-03 0:00 ` Richard Bielak 1996-04-04 0:00 ` Don Harrison 1996-03-28 0:00 ` Real OO Joachim Durchholz 1996-03-29 0:00 ` Norman H. Cohen 1996-03-30 0:00 ` John G. Volan 1996-03-26 0:00 ` Jon S Anthony 1996-03-29 0:00 ` Joachim Durchholz 1996-04-04 0:00 ` Don Harrison 1996-04-04 0:00 ` Dominique Colnet 1996-04-04 0:00 ` Steve Tynor 1996-04-08 0:00 ` Norman H. Cohen 1996-04-09 0:00 ` Matt Kennel 1996-04-08 0:00 ` Matt Kennel 1996-04-09 0:00 ` Norman H. Cohen 1996-04-09 0:00 ` Robert C. Martin 1996-04-10 0:00 ` J. Kanze 1996-05-02 0:00 Bob Crispen [not found] <DoDLr7.JDB@world.std.com> [not found] ` <4if7s5$bfk@ra.nrl.navy.mil> [not found] ` <DoDqH4.29v@world.std.com> 1996-03-26 0:00 ` AdaWorks 1996-03-29 0:00 ` Brian Rogoff [not found] <JSA.96Mar13143956@organon.com> 1996-03-15 0:00 ` Don Harrison [not found] <4hneps$1238@watnews1.watson.ibm.com> [not found] ` <Do3F1K.4xG@assip.csasyd.oz> [not found] ` <4i455s$ijq@watnews1.watson.ibm.com> 1996-03-15 0:00 ` Don Harrison [not found] ` <DoBH80.9u9@world.std.com> 1996-03-15 0:00 ` Mark A Biggar 1996-03-15 0:00 ` Richard Pitre 1996-03-16 0:00 ` Des Kenny
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox