# [MC] Multiple Inhertiance

Christopher Unkel (cunkel@us.itd.umich.edu)
Wed, 10 Apr 1996 14:59:53 PDT

- Messages sorted by:[ date ][ thread ][ subject ][ author ]

    On Wed, 10 Apr 1996, Matt Pauker wrote:

    > Multiple inheritance is not an easy issue in the least, and is one of the
    > more confusing aspects of languages (Such as C++) that do support it.  In
    > my opinion, it leads to more problems than it solves.  If you have a
    > situation where you need multiple inheritance, it could probably be
    > rewritten in a better way to only require single inheritance.

    It's very hard not to agree with this.  Multiple inheritance is powerful,
    and in some ways it is intuitive (a square, for example, combines the
    properties of a rectange and a rhombus).  However, implementing it tends
    to be anything but simple -- it's almost always a mess of special cases
    and complications.

    I think that as people try to implement this, they'll run into many, many
    exceptions that nobody's thought of yet.  On the other hand, if a
    workable implementation was done, it would be pretty neat.

    MOO is designed to be easy to program, and easy to learn to program.  To
    a programmer who's just been introduced to object-oriented programming,
    single-parent inheritance can be difficult enough (remember when you were
    first learning MOO programming?  Did you really need another thing to
    deal with?)  The real question here is whether the added power of
    multiple inheritance is worth the price of the added complexity.

    That said, I have a "restriced multiple inheritance" scheme that might
    offer some of the benefits of multiple inheritance without a lot of the
    added complexity and headaches.

    The essence of the scheme is this:  we allow multiple inhertiance only if
    a the inheriting object does not end up with conflicting definitions of
    verbs or properties.  That is, if an object currently has an ancestry chain
    that defines a certain property, another ancestor that defines the same
    property *cannot* be added -- same with verbs.

    Common ancestors in multiple paths may be allowed, because they present no
    conflict in inheritance definitions.  (Doesn't matter which tree you take
    to get there, it's the same verb...) One path may define a verb on a
    common ancestor as well, in which case that verb overrides the version on
    the common ancestory.  (A side-effect of this is that an ancestor in
    multiple tress must be checked for a verb after all objects in any path
    there -- for example:

                                     |
                                     A
                                    / \
                                  /     \
                                 B       C     D
                                 |        \   /
                                 |         \ /
                                 E          F
                                 |          |
                                 |          |
                                 G          H
                                  \        /
                                    \    /
                                      \/
                                      I

    When looking for a verb on I, we must look for it on G, E, B, H, F, and
    C *before* we look for it on A.  Since only one path can define the verb,
    it doesn't matter whether we check G H E F B C or G E B H F C, so long as
    we check B after E after G, and C after F after H, and D after F (we can
    even check D after A if we want to).

    Furthermore, if the verb is defined on B, E, or G, it may not be defined
    on H, F, C or D, but it may be defined on A or in A's ancestry.  If a
    verb is defined on D, it may not be defined on G, E, B, C, A, or in A's
    ancestry.

    Now that I write this out I'm hesitant to allow multiple paths to an
    ancestor, since my goal is simplicity.  What do other people think?
    [And now people should see why some people say that multiple inheritance
    is complicated -- my example isn't even that messy of a case...]

    As a final feature, we may want to specify that a specific verb or
    property does not propogate.  For example, we've got an object that has
    some stuff that we want descendants to inherit, and then we've got a verb
    that prevents anyone from, for example, moving it, that we don't want to
    propogate because we expect it would conflict with another ancestry
    tree.

    On the other hand, we might be able to avoid this with a well-designed
    common ancestor.

    What does this buy us?

    One thing I've thought of:

    The current standard thing that avoids multiple inheritance but still
    allows adding arbitrary neat things to players is feature objects.  This
    scheme gives us the essential equivalent of those -- we can have fertile
    objects, with no ancestors or descended from the root class, that have the
    verbs on them that we want to add players.  But, we gain the following
    benefits:

    1) The object can add properties to the player.  How many times have we
    wished for an easy way for an FO to keep information about each player?
    Either the FO need to keep the information on itself (with associated
    byte-quota difficulties, etc.) or the FO needs to add the property on the
    player, for which it needs permissions.  Messy.

    2) It's faster.  Currently, selection of verbs on FOs is done by the
    MOO-code in :huh and related verbs.  With multiple inheritance it can be
    done with compiled C code in the server, which is *much* faster.

    3) It simplifies permissions checks on those verbs.  Since they're verbs
    genuinely in the ancestry, we can set them -x if we want to, or put a
    standard permission check in them, and then the player can enhance them
    and call pass() just as with a normal verb now.

    So, what do people think of this?  Is it still too complicated/messy?
    Have I missed something really big, or a slew of minor details that make
    it not worth it?  I just thought of it, so I'm not so sure that it's a
    good or workable idea, but it does seem to be worth some thought...

    Now, returning to other scheme:

    >
    > >*  When a verb is called (foo:bar), a list of objects is generated.  This list
    > >   will roughly be the contents of a breadth-first traversal of the parentage
    > >   tree, generated as follows:
    > >
    > >   First, add the object foo with.  Then add each of the objects in
    > >   foo.parents with now, take each of their parents, and add them to the list.
    > >   Continue until all have hit #-1.
    > >
    > >   This technique, however, produces repeats in the list.  Here's how they
    > >   will be dealt with: during the ranked_parent list compilation stage, if a
    > >   repeats is found, the first occurrence is removed from the list, and the
    > >   object ends up at the end of the list.
    > >
    > >   In the end, a list of objects beginning with the object, and ending with #1
    > >   will have been generated.
    > >
    > >*  Using this list, each object is scanned for "bar", until it is found.  If it
    > >   is NOT found, a E_VERBNF is raised.  Partial verbs (bar*) will be matched at
    > >   the same time, as they have the same priority as unambiguous ones.
    >

    > What if you find more than one object with a matching verb?  You didn't
    > handle this (common) instance.

    That's what the .ranked_parent list was for.

    The way we match verbs is analagous same that we currently do.
    Currently, we go through a list of objects, starting with the object, and
    then its parent, its grandparent, etc., until we get to #-1.  On each
    object, we look for the matching verb:  if called from the command line,
    with matching args, if called from another verb, with the x bit set, etc.

    Now, instead of using the parent hierarchy, we use the .ranked_parents.
    We do exactly the same thing we do now, but with a different list of
    objects.  And, as we do now, we do the same thing for pass()ing,
    command-line matching, or straight verb-calls, just that we're looking
    for different properties in a verb.

    > You haven't handled properties, which could be difficult to work with in a
    > multiple inheritance situation.  If an object has multiple parents, which
    > properties do you plan on using if there's overlap?  These kind of issues
    > are difficult.

    The hard part isn't whether to keep the property -- you use all of the
    props defined on any ancestor; it's the permissions on it.  If two
    ancestors define a !c prop, who owns it on the child?

    ...next message...

    On Wed, 10 Apr 1996, Seth I. Rich wrote:

    > I notice that your design doesn't support multiple inheritance for command-
    > line verbs.  Why not?

    I don't understand why you think this is true.  We do the same thing for
    any verb call, command-line, passed(), called from built-in function, or
    called from verb -- but we're just looking for different things in the
    verb.  What's changed is the list of objects that we check for those
    things.  Maybe I'm reading the proposal differently than you...

    > I think that once again this is real bad design; the MOO server isn't
    > designed for multiple inheritance and this will be SLOW SLOW SLOW SLOW
    > in production use.

    Someone with more in-depth knowledge of how the server works may correct
    me here, but:

    It seems to me that it doesn't really uniquely add overhead in the case
    of most verb calls.  We're doing the same thing we do now, but instead of
    working up the inheritance hierarchy, we're working down the
    .ranked_parents property -- all in nice fast C code.  What would make it
    slow is if the inheritance tree for an object became very complicated and
    the .ranked_parents property very lengthy, but the same thing happens now
    if an object has a long list of parents back to #-1.  (Admittedly,
    allowing multiple inheritance will tend to make the ancestry list longer,
    but many of those ancestors might replace FOs where we'd end up doing the
    same thing, but with slow MOO code instead of fast C code.)  What made
    the verbified-properties so slow was that it resulted in lots of extra
    invokations of the MOO virtual machine, which is relatively slow.

    Of course, the scheme adds a *LOT* of overhead to anything that modifes
    the inheritance hierarchy.

      --Chris                           cunkel@engin.umich.edu
        ResComp Network Support Technician, Bursley Hall
        "Invisibility is in the eye of the beholder."
        Home Page: http://www-personal.engin.umich.edu/~cunkel/