Contents

Inheritance

Inheritance

Classes can inherit from multiple parent classes. Fields of the parent classes are “merged” into the resulting class by a process which, firstly, involves linearizing the graph of superclasses (ie: flattening it into a simple list). After this is done, the resulting list of classes is processed in order, adding fields as they occur. Fields which come first take priority; they override fields which come later in the list.

The algorithm used to perform the flattening is described below.

Restrictions

There are some restrictions on what can be inherited, as follows.

Field name clashes

It may be that a field name is encountered more than once whilst traversing the flattened list of parent classes. If this happens, then the first occurence is always the one that is inherited into the resulting class. However, a compilation error may also be signalled. This will happen UNLESS

Looked at from another viewpoint, this table shows the permissible combinations, with an ‘X’ meaning disallowed and a ‘+’ meaning allowed.

First def’n Instance var Static var Instance method Static method
Subsequent def’n
Instance var X X X X
Static var X + X +
Instance method X X + X
Static method X + X +

Implemented classes

This term is used to describe the set of classes in the list created by the linearization algorithm. For any class, it will comprise :-

So if we have the following class hierarchy :-

class Animal()
class Pet()
class Mammal(Animal)
class Dog(Mammal,Pet)

Then the implemented classes of Dog are Dog, Mammal, Animal and Pet.

If we have an instance of Dog, then the builtin function is() will succeed for just the implemented classes. Thus :-

d := Dog()
is(d, Pet)  # Yes
is(d, Cat)  # No
is(d, Mammal) # Yes
... etc

The implemented classes of a particular class can be easily enumerated using the library function Class.get_implemented_classes. Note that this method doesn’t produce the classes in any specific order.

Accessing overridden methods

It is often the case that a class overrides an instance method, but still wants to access the overridden method in the parent class. This can be done using the following syntax :-

import io

class Parent()
   public func()
      write("Parent.func")
   end
end

class Child(Parent)
   public override func()
      write("Child.func")
      # Call the overridden method
      Parent.func()
   end
end

procedure main()
   local z
   z := Child()
   z.func()
end

The expression Parent.func() is used to invoke the overridden method. Such an expression can only be used from within an instance method, and the target method must be in one of the instance’s implemented classes.

Linearization algorithm

Object Icon uses the C3 superclass linearization algorithm to calculate the implemented classes of a class. The algorithm turns the inheritance graph of the class into a simple list of classes, which can then be used to calculate the fields of the class.

This algorithm is also used by several other languages which support multiple inheritance.

ieval can be used to see how this algorithm is applied to a particular class. For example :-

$ ieval -i io
> c3(PipeStream)
C3: io.PipeStream(io.FileStream)
   C3: io.FileStream(io.DescStream)
      C3: io.DescStream(io.Stream, lang.NoCopy)
         C3: io.Stream(util.HasMode)
            C3: util.HasMode() = util.HasMode
            Added: io.Stream
            Added: util.HasMode
         Result: 2 classes
         C3: lang.NoCopy(lang.SelfClone, lang.Unencodable)
            C3: lang.SelfClone(lang.ObjectClone)
               C3: lang.ObjectClone() = lang.ObjectClone
               Added: lang.SelfClone
               Added: lang.ObjectClone
            Result: 2 classes
            C3: lang.Unencodable(lang.ObjectCodec)
               C3: lang.ObjectCodec() = lang.ObjectCodec
               Added: lang.Unencodable
               Added: lang.ObjectCodec
            Result: 2 classes
            Added: lang.NoCopy
            Added: lang.SelfClone
            Added: lang.ObjectClone
            Added: lang.Unencodable
            Added: lang.ObjectCodec
         Result: 5 classes
         Added: io.DescStream
         Added: io.Stream
         Added: util.HasMode
         Added: lang.NoCopy
         Added: lang.SelfClone
         Added: lang.ObjectClone
         Added: lang.Unencodable
         Added: lang.ObjectCodec
      Result: 8 classes
      Added: io.FileStream
      Added: io.DescStream
      Added: io.Stream
      Added: util.HasMode
      Added: lang.NoCopy
      Added: lang.SelfClone
      Added: lang.ObjectClone
      Added: lang.Unencodable
      Added: lang.ObjectCodec
   Result: 9 classes
   Added: io.PipeStream
   Added: io.FileStream
   Added: io.DescStream
   Added: io.Stream
   Added: util.HasMode
   Added: lang.NoCopy
   Added: lang.SelfClone
   Added: lang.ObjectClone
   Added: lang.Unencodable
   Added: lang.ObjectCodec
Result: 10 classes
> 

Each line beginning “C3:” represents a call to the algorithm. For a class with no superclasses, the algorithm just returns the class itself as the result. util.HasMode is an example, and the result is shown on one line :-

   ...
   C3: util.HasMode() = util.HasMode
   ...

For classes with superclasses, the algorithm first calls itself recursively to linearize each superclass. So for example, lang.NoCopy has two superclasses. These are shown in parentheses as follows :-

   ...
   C3: lang.NoCopy(lang.SelfClone, lang.Unencodable)
   ...

After this line the calls to the two superclasses are listed, with an extra indentation. Once those calls are complete, the results are merged together to give the result for the class itself. As each result is added by the merge, it is shown on a line by itself; finally the number of classes in the result is shown, at the same indentation as the original “C3:” line. So the output for lang.NoCopy is :-

   C3: lang.NoCopy(lang.SelfClone, lang.Unencodable)
      C3: lang.SelfClone(lang.ObjectClone)
         C3: lang.ObjectClone() = lang.ObjectClone
         Added: lang.SelfClone
         Added: lang.ObjectClone
      Result: 2 classes
      C3: lang.Unencodable(lang.ObjectCodec)
         C3: lang.ObjectCodec() = lang.ObjectCodec
         Added: lang.Unencodable
         Added: lang.ObjectCodec
      Result: 2 classes
      Added: lang.NoCopy
      Added: lang.SelfClone
      Added: lang.ObjectClone
      Added: lang.Unencodable
      Added: lang.ObjectCodec
   Result: 5 classes

At the end of the merge for PipeStream we see that ten classes are added, and these constitute the implemented classes for PipeStream, in that order. The fields of PipeStream are then calculated from those classes by traversing the list. The result of that can also be shown by ieval, as shown below :-

$ ieval -i io
> dir(PipeStream)
class io.PipeStream(io.FileStream)
   at line 1077 in /home/rparlett/objecticon/lib/main/streams.icn
   implements io.Stream, io.DescStream, io.FileStream, io.PipeStream, lang.ObjectClone, lang.SelfClone, lang.NoCopy, lang.ObjectCodec, lang.Unencodable, util.HasMode
adjust_mode        io.FileStream       10    Method Private
ALL                io.Stream           46    Public Static Const
can                util.HasMode        58    Method Public
chdir              io.FileStream       22    Method Public Native
close              io.FileStream       25    Method Public Native Override
copy_to            io.Stream           57    Method Public
create_for_fd      io.FileStream        9    Method Private Static
dflag              io.DescStream       33    Method Public Native
dup                io.PipeStream        3    Method Public Override
dup2               io.DescStream       34    Method Public
dup2_impl          io.DescStream       35    Method Private Native
dup_impl           io.DescStream       36    Method Protected Native
fd                 io.DescStream        1    Protected
...
(omitted for brevity)
...
stderr             io.FileStream        8    Public Static Const
stdin              io.FileStream        6    Public Static Const
stdout             io.FileStream        7    Public Static Const
tell               io.FileStream       20    Method Public Native Override
TRUNCATE           io.Stream           44    Public Static Const
truncate           io.FileStream       21    Method Public Native Override
ttyname            io.FileStream       24    Method Public Native
WRITE              io.Stream           42    Public Static Const
write              io.Stream           55    Method Public
write1             io.Stream           53    Method Public
writes             io.Stream           54    Method Public
writes1            io.Stream           52    Method Public
wstat              io.DescStream       39    Method Public Native
> 

The second column in the field table gives the source class for each field.

Contents