comp.lang.ada
 help / color / mirror / Atom feed
* Re: MI - clutching at straws
@ 1992-09-09 14:24 spool.mu.edu!darwin.sura.net!Sirius.dfn.de!Urmel.Informatik.RWTH-Aachen.D
  0 siblings, 0 replies; 5+ messages in thread
From: spool.mu.edu!darwin.sura.net!Sirius.dfn.de!Urmel.Informatik.RWTH-Aachen.D @ 1992-09-09 14:24 UTC (permalink / raw)


In article 92Sep8135021@Dr_No.mitre.org, eachus@Dr_No.mitre.org (Robert I. Each
us) writes:
>   A design which contains objects which are simultaneously in lists
>and trees is not inherently a bad design.  But if you need to do it,
>the programmer needs either to develop a set of higher level
>abstractions or pay attention to details when an object is say removed
>from a list.  (If the object is also an interior node in the tree, do
>you have to delete it from the tree first, or just remember its
>children and reinsert them? Is it legal to have objects that are in a
>list but not in a tree, or vice-versa? And so on.)  It can be done,
>BUT if list and tree primitives are separately inherited the
>programmer has to do most of the work.  A much better approach would
>be to use single inheritance from a tree type, and extend it with list
>primitives which are sensitive to the tree structure.  The progammer
>must still answer the same questions, but only in one place--the
>definition of the list type.
>
>     So what I was really trying to say was that in this example it is
>dificult to compare relative advantages and disadvantages of different
>MI approaches, since any SI approach is so much cleaner.

Well, I agree with you in the list/tree example. But, if you remember
my first post, I already excluded such cases where MI is used to combine
independent classes. This is not only dangerous, it is also a design
which can be easily implemented without MI (and without inheritance
at all).

Unfortunately, your arguments don't hold in my example, where several
extensions of the same base type are combined. The resulting type is
essentially the base type augmented by an arbitrary subset of the possible
extensions. The problems you mentioned cannot arise in this context.
Furthermore, it is pretty awkward to simulate such behaviour without MI.

In your previous post, you wrote:

>In article <1992Aug31.153641.29954@Urmel.Informatik.RWTH-Aachen.DE> >pk@rwthi3
.informatik.rwth-aachen.de (Peter Klein) writes:
>
>   In a more general sense, it cannot be reasonable to extend a basic type
>   by inheritance or by genericity depending on the fact whether you want
>   to combine some extensions later on. If you think that the extensions
>   should be done by inheritance, you'll end up missing MI if you want to
>   combine extensions on a higher level. On the other hand, if you say that
>   the extensions should be done using genericity, you won't need MI. But
>   in this case, you don't need inheritance at all.
>
>   Close.  IMHO inheritance is one of the least important OO features
>being added to Ada 9X, but as I mentioned above it does have its uses.
>In Ada 9X as in Ada 83, generics will be a much more powerful tool,
>and should be the tool of choice in many areas where inheritance is
>naturally used in other OO languages.

Not true. Inheritance is the more powerful feature. Dynamical binding cannot
be done with genericity; you'll never get true polymorphism without
inheritance. But I don't think this is the question. Genericity and
inheritance both have their uses, and most of the time the design makes
clear which of them is appropriate in a given situation. If you think that
inheritance is not *really* needed, I can accept that. But my original
question was: What do I loose when I have SI only? In what way does the
lack of MI affect the usefulness of the whole inheritance idea? *If* I
think extensions of a base type should be done using inheritance, how do
I solve the problem of combining independent extensions afterwards?

Peter

---
Peter Klein                        E-Mail: pk@rwthi3.informatik.rwth-aachen.de
Lehrstuhl fuer Informatik III      Tel.: +49/241/80-21320
Ahornstrasse 55                    Fax.: +49/241/80-21329
RWTH Aachen
D-5100 Aachen
Germany

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: MI - clutching at straws
@ 1992-09-11  6:33 cis.ohio-state.edu!news.sei.cmu.edu!ajpo.sei.cmu.edu!goodsenj
  0 siblings, 0 replies; 5+ messages in thread
From: cis.ohio-state.edu!news.sei.cmu.edu!ajpo.sei.cmu.edu!goodsenj @ 1992-09-11  6:33 UTC (permalink / raw)


eachus@Dr_No.mitre.org (Robert I. Eachus) writes:

>
>   >>A program which has objects which are simultanously in lists and trees,
>   >>and uses MI to manage both structures is a mess.  Such a program can be
>   >>created and made to work, but the list primitives will have to be aware
>   >>of the tree primitives and vice-versa.
>
>   > It's not very persuading to point out a poor design and then state
>   > that the technology was the fault.
>
>   I think you are agreeing with me, but the quote out of context
>seems to say something I didn't intend...
>
>A design which contains objects which are simultaneously in lists
>and trees is not inherently a bad design.  


No, but your intent to insist that the 2 classes must be "aware"
of each other is what I refer to.  Your arbitrary choice of a poor
design example for MI does not invalidate the usefulness of MI.

>But if you need to do it,
>the programmer needs either to develop a set of higher level
>abstractions or pay attention to details when an object is say removed
>from a list.  (If the object is also an interior node in the tree, do
>you have to delete it from the tree first, or just remember its
>children and reinsert them? Is it legal to have objects that are in a
>list but not in a tree, or vice-versa? And so on.)  It can be done,
>BUT if list and tree primitives are separately inherited the
>programmer has to do most of the work.  A much better approach would
>be to use single inheritance from a tree type, and extend it with list
>primitives which are sensitive to the tree structure.  The progammer
>must still answer the same questions, but only in one place--the
>definition of the list type.
>

There is no need to confuse the *insertion* of an object into
multiple containers with multiple inheritance.  To argue this silly
example is moot.  You need to come up with a valid MI example before
you start knocking the usefulness of MI.  -- Bob Crispen -- where's that
MI summary post ?

>
>     So what I was really trying to say was that in this example it is
>dificult to compare relative advantages and disadvantages of different
>MI approaches, since any SI approach is so much cleaner.

I no way have you shown the SI approach to be much cleaner. Misleading
statements like this are of no usefulness in the pursuit of the MI truth.
To insist that inheritance only exist within single threads is somewhat
fascist, IMHO.

Use a little Jeet Kun Do:  "Absorb what is useful, disregard what is not"

It is silly to say multiple inheritance is *never* useful.  Just as it is 
silly to say single inheritance is *always* cleaner.  Neither statement is
correct.  The correct answer is:  Use MI when it makes sense, use SI when
it makes sense.  The 2 concepts are not contradictory.  

>--
>
>                                        Robert I. Eachus
>
>with STANDARD_DISCLAIMER;

28~
John Goodsen
goodsenj@ajpo.sei.cmu.edu
-- 
John Goodsen                              Ada Joint Program Office
jgg@evb.com                               PCIS Programme
800-877-1815                              goodsenj@ajpo.sei.cmu.edu

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: MI - clutching at straws
@ 1992-09-14 20:19 Robert I. Eachus
  0 siblings, 0 replies; 5+ messages in thread
From: Robert I. Eachus @ 1992-09-14 20:19 UTC (permalink / raw)


In article <1992Sep9.142434.3940@Urmel.Informatik.RWTH-Aachen.DE> pk@rwthi3.inf
ormatik.rwth-aachen.de (Peter Klein) writes:

 > Unfortunately, your arguments don't hold in my example, where several
 > extensions of the same base type are combined. The resulting type is
 > essentially the base type augmented by an arbitrary subset of the possible
 > extensions. The problems you mentioned cannot arise in this context.
 > Furthermore, it is pretty awkward to simulate such behaviour without MI.

    But it is exactly that case...the (base) type is extended with lists
and trees, and then these extensions are combined.  (It is hard not to
use nasty words to relieve frustration at this point, but I know that
this is a hard enough to understand mechanisms like type extension and
inheritance, and the subtile differences in mechanisms discussed here
tend to rapidly convert any brain into jelly, but bear with me.)
   
    Part of the problem is semantic.  I think Tucker will be glad to
explain to you how Ada implements multiple inheritance, while most of
us use MI to mean something other than generic instantiation, but
Tucker does have a point.  The Ada 9X mechanisms are safer and more
general than those offered by most instances of MI, but it does
require more thinking up front by the designer of a mix-in.

 > Not true. Inheritance is the more powerful feature. Dynamical binding cannot
 > be done with genericity; you'll never get true polymorphism without
 > inheritance. But I don't think this is the question. Genericity and
 > inheritance both have their uses, and most of the time the design makes
 > clear which of them is appropriate in a given situation. If you think that
 > inheritance is not *really* needed, I can accept that. But my original
 > question was: What do I loose when I have SI only?

     I assume that you left a word out above, since in my post I did
say that inheritance is sometimes necessary, it is union style
multiple inheritance that is unnecessary in Ada or any other language
with sufficiently powerful generic capabilities.

 > In what way does the lack of MI affect the usefulness of the whole
 > inheritance idea? *If* I think extensions of a base type should be
 > done using inheritance, how do I solve the problem of combining
 > independent extensions afterwards?

     You don't.  But in general it is always wrong to combine
extentions which were not intended to be combined.  Ada 9X will give
you a choice of several combining styles, and in general the language
will insure that mixtures which are permitted will work.  However,
since generics mean that the difference between a combining extension
and normal single inheritance is a few words, you should honor the
wishes of the type designer and assume that two types which can't be
mixed shouldn't be mixed:

      with Parent;
      package New_Abstraction is
        type Child is new Parent.Abstraction with private;
        -- syntax under construction, very subject to change.
        ...
      private
        ...
      end New_Abstraction;

      with Parent;
      generic
        type Abstraction is new Parent.Abstraction;
        -- see note above on syntax
      package MixIn is
        type Child is new Abstraction with private;
        ...
      private
        ...
      end MixIn;

     Now New_Abstraction uses single inheritance and can't mix with
another similar extension. MixIn, with just a few lines different in
its specification, can mix with similar type extensions OR with
New_Abstraction: 

     with New_Abstraction, MixIn;
     package Mixed_In is new MixIn(New_Abstraction.Child);

     (Note that if you do this, the body of MixIn can access the
operations of its parent, in this case New_Abstraction.Child, as can
users who with Mixed_In.  However, if Mixed_In hides an operation of
New_Abstraction.Child, whether or not dispatching is involved, the user
will have to with New_Abstraction and do a type conversion to access
the "parent" operation.)

     If it weren't for the fact that generics are IMHO a much more
powerful tool than inheritance, I could argue that using generics to
implement mix-ins is like using a sledgehammer to swat flies.  But
since Ada has all of the necessary support for generics for other
reasons, it seems silly to talk about adding MI because maybe someday
someone will think of an application where classical MI is
significantly better.  (In my opinion, and that of a lot of other
software engineers, the ability to express "Watch it!" in the code is
a definite advantage.  In Ada 9X using inheritance in a non-generic
fashion will be a strong warning message that this new type has more
than mix-in semantics.)

--

					Robert I. Eachus

with STANDARD_DISCLAIMER;
use  STANDARD_DISCLAIMER;
function MESSAGE (TEXT: in CLEVER_IDEAS) return BETTER_IDEAS is...

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: MI - clutching at straws
@ 1992-09-15 14:08 Peter Klein
  0 siblings, 0 replies; 5+ messages in thread
From: Peter Klein @ 1992-09-15 14:08 UTC (permalink / raw)


In article 92Sep14151942@Dr_No.mitre.org, eachus@Dr_No.mitre.org (Robert I. Eac
hus) writes:
>In article <1992Sep9.142434.3940@Urmel.Informatik.RWTH-Aachen.DE> pk@rwthi3.in
formatik.rwth-aachen.de (Peter Klein) writes:
>
> > Unfortunately, your arguments don't hold in my example, where several
> > extensions of the same base type are combined. The resulting type is
> > essentially the base type augmented by an arbitrary subset of the possible
> > extensions. The problems you mentioned cannot arise in this context.
> > Furthermore, it is pretty awkward to simulate such behaviour without MI.
>
>    But it is exactly that case...the (base) type is extended with lists
>and trees, and then these extensions are combined.  (It is hard not to
>use nasty words to relieve frustration at this point, but I know that
>this is a hard enough to understand mechanisms like type extension and
>inheritance, and the subtile differences in mechanisms discussed here
>tend to rapidly convert any brain into jelly, but bear with me.)
>  
Well, either you didn't understand my example or I didn't understand
yours :). Here's my understanding of what you said: You have some
base type, say X. Now you make up lists and trees which *contain* entries
of type X. Right? But then, neither the list nor the tree class are
subtypes of X. They are containers with no common base class and therefore
cannot be "merged" in my proposal.

The other possibility would be that your base class is a container class
like lists, and then you make subtypes for trees and sets. In this case,
it is first of all doubtful that inheritance should be used at all to
design this (since the inheritance is completely on the implementation
part; this is more likely a simple import). Anyway, if you insist to do
so, you could make a class then which is both a set and a tree. But who
would want to do so?

>    Part of the problem is semantic.  I think Tucker will be glad to
>explain to you how Ada implements multiple inheritance, while most of
>us use MI to mean something other than generic instantiation, but
>Tucker does have a point.  The Ada 9X mechanisms are safer and more
>general than those offered by most instances of MI, but it does
>require more thinking up front by the designer of a mix-in.
>
> > Not true. Inheritance is the more powerful feature. Dynamical binding canno
t
> > be done with genericity; you'll never get true polymorphism without
> > inheritance. But I don't think this is the question. Genericity and
> > inheritance both have their uses, and most of the time the design makes
> > clear which of them is appropriate in a given situation. If you think that
> > inheritance is not *really* needed, I can accept that. But my original
> > question was: What do I loose when I have SI only?
>
>     I assume that you left a word out above, since in my post I did
>say that inheritance is sometimes necessary, it is union style
>multiple inheritance that is unnecessary in Ada or any other language
>with sufficiently powerful generic capabilities.
>
Hmm, isn't this exactly what I said? I mean, that MI shouldn't be used to
combine arbitrary classes. Or what do you mean by "union style MI"?

> > In what way does the lack of MI affect the usefulness of the whole
> > inheritance idea? *If* I think extensions of a base type should be
> > done using inheritance, how do I solve the problem of combining
> > independent extensions afterwards?
>
>     You don't.  But in general it is always wrong to combine
>extentions which were not intended to be combined.  Ada 9X will give
>you a choice of several combining styles, and in general the language
>will insure that mixtures which are permitted will work.  However,
>since generics mean that the difference between a combining extension
>and normal single inheritance is a few words, you should honor the
>wishes of the type designer and assume that two types which can't be
>mixed shouldn't be mixed:
>
> [ ..example deleted.. ]
>
Yes and no. It is important to make a distinction between combining
arbitrary extensions and extensions which should not be combined. IMHO,
it is illegal to state that *if* I want to combine extensions, these
possible extensions must be known to the base type. It's a quite
natural situation that I want extensions to be mixable, but I cannot
predict how many extensions there will be.

Another example: In a tree, I have nodes. Every node is member of a node
class. This class determines which attributes the node carries. Now, the
classes are arranged into a hierarchy. I might want to build abstract
syntax trees using this; with node classes for identifiers, statements
(with appropriate subclasses for special statements) and the like.
Now there are obvious occurences of MI in this example: An applied
occurrence of an identifier in an expression is a subclass of identifier
(i.e. carries the "name-of-identifier" attribute) as well as a subclass
of the expression component, say factor (has e.g. a "value" attribute).

I hope this clarifies my point. The writer of the base class for the nodes
doesn't want to know in what way his class will be augmented; i.e. which
node classes there will be and what attributes they have. But still, the
type designer might want these to be combined.

>     If it weren't for the fact that generics are IMHO a much more
>powerful tool than inheritance, I could argue that using generics to
>implement mix-ins is like using a sledgehammer to swat flies.  But
>since Ada has all of the necessary support for generics for other
>reasons, it seems silly to talk about adding MI because maybe someday
>someone will think of an application where classical MI is
>significantly better.  (In my opinion, and that of a lot of other
>software engineers, the ability to express "Watch it!" in the code is
>a definite advantage.  In Ada 9X using inheritance in a non-generic
>fashion will be a strong warning message that this new type has more
>than mix-in semantics.)
>

Sorry, but I still don't see how you handle polymorphism with generics.
One last example on this: Suppose you have some container class, let's
say a list. In this list, you want to store objects of different types.
In the implementation of the list, you don't know what types, you don't
even know how many. In fact, you *shouldn't* know this, because it has
nothing to do with the list itself. Now, please, how do you do this with
generics?

Peter
---
Peter Klein                        E-Mail: pk@rwthi3.informatik.rwth-aachen.de
Lehrstuhl fuer Informatik III      Tel.: +49/241/80-21320
Ahornstrasse 55                    Fax.: +49/241/80-21329
RWTH Aachen
D-5100 Aachen
Germany

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: MI - clutching at straws
@ 1992-09-18 22:08 Robert I. Eachus
  0 siblings, 0 replies; 5+ messages in thread
From: Robert I. Eachus @ 1992-09-18 22:08 UTC (permalink / raw)


In article <1992Sep15.140840.3405@Urmel.Informatik.RWTH-Aachen.DE> pk@rwthi3.in
formatik.rwth-aachen.de (Peter Klein) writes:

  > Well, either you didn't understand my example or I didn't
  > understand yours... Anyway, if you insist to do so, you could make
  > a class then which is both a set and a tree. But who would want to
  > do so?

    I think we now understand each other very well.  The "easy" case
does not require MI and is uninteresting.  The crazy case is crazy.
The interesting case is interesting, but requires a lot of thought on
the part of the programmer, and while MI might help with the busywork,
it is no substitute of thought where that is needed.

    But, surprise! I realized that there is a specific case whis is
interesting, and in which it happens that I did implement using
generics to do mix-ins.  I needed to implement two dimensional sparse
matrices, (and operations like addition on them).  The solution I
chose was to create a generic list package and instantiate it four
times.  Once to create the row operations, once to create the column
operations, and then once each to create lists of the heads of the
rows and columns.

   When I was finished with the program, I had about four different
layers of generic instantiations, since the list package wasn't the
only generic I used.  But inlining meant that the final product was at
least as fast as totally unmaintainable spaghetti code.  (And somewhat
more maintainable.  That particular program reminds me of Heaviside's
reply when a physicist complained that his papers were hard to read:
"Not nearly as hard as they are to write!")  

  > Hmm, isn't this exactly what I said? I mean, that MI shouldn't be
  > used to combine arbitrary classes. Or what do you mean by "union
  > style MI"?

   For the record, what I mean by union style MI is when neither
parent dominates the other.  This works fine for orthogonal cases, but
falls down when an ordering is needed.  Most MI languages do have a
precedence rule, but when you need to know what it says, you are
already in trouble.

  > Yes and no. It is important to make a distinction between combining
  > arbitrary extensions and extensions which should not be combined. IMHO,
  > it is illegal to state that *if* I want to combine extensions, these
  > possible extensions must be known to the base type. It's a quite
  > natural situation that I want extensions to be mixable, but I cannot
  > predict how many extensions there will be.

   I think you missed my point here.  I said that the person who
defines the extension in Ada 9X chooses whether and how it is mixable.
He does not define which other extentions it can be mixed with just
which meta-class? (I need a word here and all the normal words are
taken) of extentions it will mix with.  Some extensions only extend
members of a specific class hierarchy, others will extend members of
any class, etc.

  > ... The writer of the base class for the nodes doesn't want to
  > know in what way his class will be augmented; i.e. which node
  > classes there will be and what attributes they have. But still,
  > the type designer might want these to be combined.

    Fine.  Getting a little more specific, you may have a graph where
there are various types of nodes and arcs.  (Let's call the union of
nodes and arcs entities.)  You also have attributes some of which
apply to nodes some to arcs and some to both.  The designer of one
attibute may specify it only applies to arcs, say a length attribute,
while another attribute may apply to nodes or arcs, say color.

    All of this is very expressible with generic style mix-ins.  And
notice that none of these restrictions dealt with the specific types
that are in each class.  This speicification comes at instantiation
time, when you assign attributes to entities.  And yes, you can
determine on a case-by-case basis whether an attribute is inherited or
not.  (Attributes can be defined so that they are always inherited, or
they can be defined so that the user determines whether or not they
are inherited for that entity type.)

    Incidently, for anyone who reads this far, I regard this more as
an exploration of the Ada 9X class model than a debate.  I keep coming
up with more and more semantic points to add to my list of things to
keep the same during the standardization process.  Between the current
stage of the work and a final standard lots of things will get lost so
that the final standard is consistant and implementable.  To make sure
that no useful features get lost, you need to know what they are.  As
an example, I realized up above that there will be cases where it is
necessary to nest a generic instantiation in a package which exports a
tagged type, and then rename some of the operations to make them class
wide operations that are derived by children of the tagged type.  Neat
feature, but we need to make sure it doesn't get lost:

       package Foos is
         type Foo is new T with private;
         function Baz return Foo'CLASS;
         ...
       private

       package body Foos is
         package Bar is new Foobar(Foo'CLASS);
         function Baz return Foo'CLASS renames Bar.Baz;
       end Foos;



--

					Robert I. Eachus

with STANDARD_DISCLAIMER;
use  STANDARD_DISCLAIMER;
function MESSAGE (TEXT: in CLEVER_IDEAS) return BETTER_IDEAS is...

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~1992-09-18 22:08 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1992-09-18 22:08 MI - clutching at straws Robert I. Eachus
  -- strict thread matches above, loose matches on Subject: below --
1992-09-15 14:08 Peter Klein
1992-09-14 20:19 Robert I. Eachus
1992-09-11  6:33 cis.ohio-state.edu!news.sei.cmu.edu!ajpo.sei.cmu.edu!goodsenj
1992-09-09 14:24 spool.mu.edu!darwin.sura.net!Sirius.dfn.de!Urmel.Informatik.RWTH-Aachen.D

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