comp.lang.ada
 help / color / mirror / Atom feed
* 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
       [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

* 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

* 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

* 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
       [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   ` 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

* 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         ` Brian & Karen Bell
                           ` (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: 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         ` Brian & Karen Bell
  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, 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: 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

* 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         ` Brian & Karen Bell
@ 1996-03-20  0:00         ` David Emery
  1996-03-20  0:00           ` Mark R Okern - F95
  1996-03-20  0:00         ` Real OO Dale Stanbrough
                           ` (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

* 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: 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       ` 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         ` Dale Stanbrough
  1996-03-21  0:00           ` Richard Pitre
  1996-03-21  0:00         ` Norman Cohen giving IBM a bad name Kent Mitchell
                           ` (2 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: 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: 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         ` Real OO Dale Stanbrough
@ 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: 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: Real OO
  1996-03-21  0:00               ` Scott Leschke
  1996-03-21  0:00                 ` Robert A Duff
@ 1996-03-21  0:00                 ` Norman H. Cohen
  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: 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: 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

* 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: Real OO
  1996-03-20  0:00             ` Real OO John G. Volan
@ 1996-03-21  0:00               ` Scott Leschke
  1996-03-21  0:00                 ` Robert A Duff
                                   ` (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                 ` Robert A Duff
  1996-03-21  0:00                 ` Norman H. Cohen
  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
       [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: 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         ` Norman Cohen giving IBM a bad name 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: Real OO
  1996-03-21  0:00               ` Scott Leschke
  1996-03-21  0:00                 ` Robert A Duff
  1996-03-21  0:00                 ` Norman H. Cohen
@ 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: 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: Real OO
  1996-03-22  0:00 ` Don Harrison
@ 1996-03-22  0:00   ` Norman H. Cohen
  1996-03-27  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)


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: 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: 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-26  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)


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: 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-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-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
       [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
  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     ` 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
       [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
  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-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       ` 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
                     ` (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
       [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-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-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
       [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

* 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

* 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-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           ` Robb Nebbe
                             ` (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-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-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           ` Robb Nebbe
  1996-04-04  0:00           ` Laurent Guerby
                             ` (2 subsequent siblings)
  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-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         ` Don Harrison
                             ` (2 preceding siblings ...)
  1996-04-04  0:00           ` Tucker Taft
@ 1996-04-04  0:00           ` Jon S Anthony
  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         ` 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         ` Don Harrison
  1996-04-04  0:00           ` Robb Nebbe
  1996-04-04  0:00           ` Laurent Guerby
@ 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
  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         ` Don Harrison
  1996-04-04  0:00           ` Robb Nebbe
@ 1996-04-04  0:00           ` Laurent Guerby
  1996-04-04  0:00           ` Tucker Taft
  1996-04-04  0:00           ` Jon S Anthony
  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           ` 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   ` 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-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-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-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-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-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-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-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-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-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-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-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-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             ` Matt Kennel
                               ` (2 more replies)
  1996-04-12  0:00           ` Don Harrison
  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-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             ` Jacob Gore
  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-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-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-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           ` 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           ` 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             ` 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-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-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             ` 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-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-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-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: 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: 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-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

* 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: 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: 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: 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: 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-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-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-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-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                         ` 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                         ` 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-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-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-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-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-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-04-30  0:00                           ` Amit Patel
                                             ` (2 more replies)
  1996-05-01  0:00                         ` Don Harrison
                                           ` (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 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-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-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-04-30  0:00                       ` Robert Dewar
                                           ` (2 preceding siblings ...)
  1996-05-01  0:00                         ` Don Harrison
@ 1996-05-01  0:00                         ` AdaWorks
  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                     ` 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-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                         ` 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-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                         ` Don Harrison
  1996-05-01  0:00                           ` David Hopwood
                                             ` (2 more replies)
  1996-05-01  0:00                         ` AdaWorks
  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-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-02  0:00                       ` Don Harrison
  1996-05-02  0:00                         ` Jon S Anthony
@ 1996-05-02  0:00                         ` Robert I. Eachus
  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                         ` Jon S Anthony
  1996-05-03  0:00                           ` Don Harrison
  1996-05-02  0:00                         ` Robert I. Eachus
  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-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-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-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-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-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                         ` Jon S Anthony
                                           ` (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-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-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 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

* 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-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-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-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-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-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                         ` 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-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-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-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-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                         ` Jon S Anthony
  1996-05-02  0:00                         ` Robert I. Eachus
@ 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-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-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-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                                     ` 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-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-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-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-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-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: Ada terminology (was Re: Real OO)
  1996-05-07  0:00           ` Ada terminology (was Re: Real OO) David Hopwood
  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             ` The Right Reverend Colin James III
  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           ` Ada terminology (was Re: Real OO) David Hopwood
  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
  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: 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: 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           ` Ada terminology (was Re: Real OO) David Hopwood
@ 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
  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: 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-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-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-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-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

* 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             ` Dave Jones
                               ` (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: 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-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                       ` Robert Dewar
                                           ` (3 preceding siblings ...)
  1996-05-01  0:00                         ` AdaWorks
@ 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: 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-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-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         ` 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

* 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-09  0:00         ` Don Harrison
  1996-05-09  0:00           ` Joachim Durchholz
@ 1996-05-09  0:00           ` Jon S Anthony
  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
       [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         ` Don Harrison
@ 1996-05-09  0:00           ` Joachim Durchholz
  1996-05-09  0:00           ` Jon S Anthony
  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

* 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
  1996-05-07  0:00       ` Joachim Durchholz
@ 1996-05-09  0:00         ` Don Harrison
  1996-05-09  0:00           ` Joachim Durchholz
  1996-05-09  0:00           ` Jon S Anthony
  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-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

* 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-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-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-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-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-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-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-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                         ` Roger Browne
                                           ` (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                         ` Roger Browne
@ 1996-05-14  0:00                         ` Joachim Durchholz
  1996-05-14  0:00                         ` Don Harrison
                                           ` (3 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                         ` Roger Browne
  1996-05-14  0:00                         ` Joachim Durchholz
@ 1996-05-14  0:00                         ` Don Harrison
  1996-05-14  0:00                           ` Robert A Duff
                                             ` (3 more replies)
  1996-05-15  0:00                         ` Steve Tynor
                                           ` (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-13  0:00                       ` Tucker Taft
@ 1996-05-14  0:00                         ` Roger Browne
  1996-05-14  0:00                         ` Joachim Durchholz
                                           ` (4 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-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-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-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                           ` 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                     ` 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

* 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-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-13  0:00                       ` Tucker Taft
                                           ` (3 preceding siblings ...)
  1996-05-15  0:00                         ` Steve Tynor
@ 1996-05-15  0:00                         ` Alexander Kjeldaas
  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-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-13  0:00                       ` Tucker Taft
                                           ` (2 preceding siblings ...)
  1996-05-14  0:00                         ` Don Harrison
@ 1996-05-15  0:00                         ` Steve Tynor
  1996-05-15  0:00                         ` Alexander Kjeldaas
  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-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
                                             ` (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-13  0:00                       ` Tucker Taft
                                           ` (4 preceding siblings ...)
  1996-05-15  0:00                         ` Alexander Kjeldaas
@ 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-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-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-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

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         ` Brian & Karen Bell
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                 ` Robert A Duff
1996-03-21  0:00                 ` Norman H. Cohen
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         ` Real OO Dale Stanbrough
1996-03-21  0:00           ` Richard Pitre
1996-03-21  0:00         ` Norman Cohen giving IBM a bad name 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-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           ` Robb Nebbe
1996-04-04  0:00           ` Laurent Guerby
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-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-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                         ` 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-01  0:00                         ` AdaWorks
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                         ` Jon S Anthony
1996-05-03  0:00                           ` Don Harrison
1996-05-06  0:00                             ` Jon S Anthony
1996-05-02  0:00                         ` Robert I. Eachus
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             ` 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-12  0:00           ` Don Harrison
1996-04-12  0:00             ` Jacob Gore
1996-04-16  0:00               ` Don Harrison
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             ` 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             ` The Right Reverend Colin James III
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                         ` Roger Browne
1996-05-14  0:00                         ` Joachim Durchholz
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                         ` Steve Tynor
1996-05-15  0:00                         ` Alexander Kjeldaas
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           ` Joachim Durchholz
1996-05-09  0:00           ` Jon S Anthony
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] <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
     [not found] <JSA.96Mar13143956@organon.com>
1996-03-15  0:00 ` Don Harrison

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox