is Missing multiple inheritance in sqeak a drawback ?

David Simmons David.Simmons at smallscript.com
Wed Oct 23 21:15:46 UTC 2002


I use multiple-inheritance of "behavior" all the time in SmallScript --
it is a fundamentally utilized design feature of the system. It is
multiple-inheritance of "structure" that I find to be problematic on
many levels.

SmallScript Interfaces give the "illusion" of MI of structure, but are
in-fact just aspects/facets/roles that are aggregated [at the VM level]
on to a given object instance.

SmallScript's hierarchy looks like:

Object
    Behavior
        Constructor
            Class
            Metaclass

    Aspect [alias Role]
        Mixin
            AbstractMixin [alias Specification]
            Interface
            PureMixin

A <Constructor> determines the physical field "structure/layout" policy
of an object. A <Behavior> determines the methods available (behavior)
to an object. Behaviors support "mixing-in" of other behaviors.

<Specification> classes are what you would think of as "interfaces" in
Java. They are purely specifications. Attempting to execute a
"specification" method generates an exception. Any code in their method
body will be compiled and checked, but will otherwise serve only as a
template for toolset usage. You can declare any method in any class to
be a specification via the <$specification> directive (annotation).
Specifications cannot be instantiated.

<PureMixin> can contain only pure-behavior methods. Mixin classes are
are only allowed to have "virtual" fields declared on them. Mixins
cannot be instantiated.

<Interface> can contain concrete fields. Interfaces can be instantiated.
A given interface knows the "core" object it is an aspect of, and
therefore one can go from the object to an interface and back. Or from
one interface/aspect/role to directly to another on the same "core"
object. 

This makes interfaces very different from C++ MI. Put another way, an
interface knows about all its [possible] peers because it is always
associated with its "core-personality". Interfaces are lazily generated,
in general they only come into existence as first-class objects when a
runtime operation's semantics require them. They are managed through the
basic extensible property facilities of runtime's unified object-model.
For those familiar with COM or other aggregate models, you'll probably
immediately  recognize <Interface> as being exactly the form.

Mixins can, themselves, be mixed with other mixins.

    Mixin name: A. Mixin name: B. Mixin name: C mixins: A,B.
    Class name: Foo mixins: C.
    Eval [stdout cr << Foo supertypes].

yields: 
    DefaultProject.Foo, 
    DefaultProject.C, 
    DefaultProject.A, 
    DefaultProject.B, 
    PureMixin,
    Mixin, Aspect, Object, nil

Mixins can be dynamically added [as an aggregate whole] to any behavior.
The complexity of the mixin has no bearing on the cost to add/remove it
from a behavior -- there is effectively little or no cost to add/remove
one.
---------

SmallScript combines a number of facilities into a unified model,
specifically to address the "inheritance" issues of MI.

1. The binding-machinery is extensible and based on a predicate system.
This allows for flexibility in specification of binding scenarios such
as:
   a) only bind to instances of this class, not subclasses
   b) only bind if the receiver was the interface / not its core
   c) around, before, inner, after methods [which are "truly" different
          from statically mixed-in wrappers seen in various ST work to
date]

2. Selector-namespaces permit scoping of access to behavior, and
partitioning of behavior to support multiple versions of the same
message-form to exist on a given Behavior/Class. [SmallScript has
classes, but supports prototypes/instance-specific behavior, so I tend
to use the term behavior instead of class].

3. Modules permit partitioning into meta-namespaces allowing the loading
of multiple versions of the same entities within a given running system.

4. Interfaces support COM/C++ vtable mechanisms [transparently] for
callback behavior and correct bindings to <self> vs <thisInterface>
structures.

5. Optional typing, and mixins as behaviors work hand in hand to provide
runtime enforced contract verifaction.

    Method [foo: <SomeMixin> arg ...body...]

    Where we can declare that an argument must conform to a
    mixin/interface/role/aspect, or it will not bind to the
    given method.

6. The concept of virtual fields and properties -- similar to the
self-language notion that a slot-name and a message-send are the same. A
virtual field is one which behaves for all intents and purposes as if it
were a "concrete" slot/struct field on an object. But, it will actually
only be created when required, and will be created as a dynamically
added property on a given object instance. Since field access is
logically managed through messaging, if a class declares a "concrete"
version of a field that was mixed-in as a virtual field, then the
"concrete" version will be used as the data store.

========= PURE MIXIN VERSION =========

    Mixin name: Example fields: auto virtual property a,b
    {
        Method [combineFields ^a+b]
        Method [b ^b ? ['(default-b)']]
        Method [a ^a ? ['(default-a)']]    
        Method [
        display(ident)
            stdout.printf('`n%5c %s %5c`n"a+b": %s', $-, 
                ident, $-, combineFields()).
            |target| := self.
            target.basicNamedSlotSize timesRepeat: [:slotIndex|
                stdout.printf('`n  [%s]: %s', slotIndex,
                    target.basicNamedSlotAt(slotIndex)).
            ].
        ].
    }

    Class name: Foo fields: auto a mixins: Example.

    Eval [
        |foo| := Foo().
        foo.display(1).
        foo a: '(our-assigned-a)'; display(2).
        foo b: '(our-assigned-b)'; display(3).
    ]
 
 OUTPUT:   
    ----- 1 -----
    "a+b": (default-b)
      [1]: nil
    ----- 2 -----
    "a+b": (our-assigned-a)(default-b)
      [1]: (our-assigned-a)
    ----- 3 -----
    "a+b": (our-assigned-a)(our-assigned-b)
      [1]: (our-assigned-a)
     
========= INTERFACE VERSION =========

    Interface name: Example fields: auto property a,b
    {
        Method [combineFields ^a+b]
        Method [b ^b ? ['(default-b)']]
        Method [a ^a ? ['(default-a)']]    
        Method [
        display(ident)
            stdout.printf('`n%5c %s %5c`n"a+b": %s', $-, 
                ident, $-, combineFields()).
            |target| := self.
            target.class.fieldsDo: [:field|
                stdout.printf('`n  [%s]: %s', field.name,
                    target.basicNamedSlotAt(field.slotIndex)).
            ].
        ].
        Method [<$interface-only>
        display(ident)
            display(ident).
            stdout cr << '  => interface invoked info <='.
            |target| := thisInterface.
            target.class.fieldsDo: [:field|
                stdout.printf('`n  [%s]: %s', field.name,
                    target.basicNamedSlotAt(field.slotIndex)).
            ].
        ]    
    }

    Class name: Foo fields: auto a mixins: Example.

    Eval [
        |foo| := Foo().
        foo.display(1).
        foo a: '(our-assigned-a)'; display(2).
        foo b: '(our-assigned-b)'; display(3).
        
        |iface| := foo as: Example.
        stdout << '`n--`n' << foo.class.
        stdout << '`n--`n' << iface.class.
        iface.display(4).
        foo b: '(our-second-assigned-b)'.
        foo.display(5).
        iface.display(6).
    ]
    
OUTPUTS:
    ----- 1 -----
    "a+b": (default-b)
      [a]: nil
    ----- 2 -----
    "a+b": (our-assigned-a)(default-b)
      [a]: (our-assigned-a)
    ----- 3 -----
    "a+b": (our-assigned-a)(our-assigned-b)
      [a]: (our-assigned-a)
    --
    DefaultProject.Foo
    --
    DefaultProject.Example
    ----- 4 -----
    "a+b": (our-assigned-a)(our-assigned-b)
      [a]: (our-assigned-a)
      => interface invoked info <=
      [a]: nil
      [b]: (our-assigned-b)
    ----- 5 -----
    "a+b": (our-assigned-a)(our-second-assigned-b)
      [a]: (our-assigned-a)
    ----- 6 -----
    "a+b": (our-assigned-a)(our-second-assigned-b)
      [a]: (our-assigned-a)
      => interface invoked info <=
      [a]: nil
      [b]: (our-second-assigned-b)
    
There are many more things that could be explained/discussed here, but I
have run out of time for this post. 

I have now had quite a few years of working with this "form of MI" in
Smalltalk and have found it to be clean, simple, and efficient. It is
wholly consistent with dynamic language concepts where these "mixins"
can be added or removed on the fly [the cost to remove an entire mixin
hierarchy is about the same as adding or removing a CompiledMethod from
a method-dictionary].

<g> I have also mapped it directly onto other runtimes like .NET.

The key element to supporting MI is to recognize the "pure" object model
of Smalltalk is based on messaging (behavior); it is crucial to keep
this concept distinct from that of "structure" (representation) of
objects.

-- Dave S. [SmallScript Corp]

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SmallScript for the AOS & .NET Platforms
David.Simmons at SmallScript.com
http://www.smallscript.org


> -----Original Message-----
> From: squeak-dev-admin at lists.squeakfoundation.org [mailto:squeak-dev-
> admin at lists.squeakfoundation.org] On Behalf Of Hans Beck
> Sent: Friday, October 11, 2002 11:32 AM
> To: Squeak-Dev
> Subject: is Missing multiple inheritance in sqeak a drawback ?
> 
> Hi,
> 
> I'm using squeak for prototyping our LCD measurement system software.
>  From time to time, some colleagues look at my display and are
> astonished ;-)) But they are c++ freaks, and if we discuss about the
> usability  of squeak for prototyping, always there comes the argument
of
> lacking multiple inheritance.
> 
> So because I'm not a theoretican for computer languages but  I suppose
> there are a lot very good guys on this list, I would ask the
community,
> how far multiple inheritance is really needed, or is it bad or what
ever
> ? (Looking from a more or less theoretical viewpoint of object
oriented
> concepts ) ?
> 
> Thanks for help finding arguments :-))
> 
> Greetings
> 
> Hans
> 





More information about the Squeak-dev mailing list