Chapter 4. Code Inclusion and Partial Classes

Table of Contents
4.1. Include Clauses
4.2. Partial Classes and Stub routines

Object oriented languages usually support the derivation of new classes by inheriting from existing classes and modifying them. In Sather, the notion of inheritance is split into two separate concepts - type relations between classes and code relations between classes. In this chapter we will deal with the latter (and simpler) concept, that of reusing the code of one class in another. We refer to this as implementation inheritance or code inclusion.

4.1. Include Clauses

The re-use of code from one class in another class is defined by include clauses. These cause the incorporation of the implementation of the specified class, possibly undefining or renaming features with feature modifier clauses. The include clause may begin with the keyword 'private', in which case any unmodified included feature is made private.
include A a->b, c->, d->private d;
private include D e->readonly f;

Code inclusion permits the re-use of code from a parent concrete class in child concrete class . Including code is a purely syntactic operation in Sather. To help illustrate the following examples, we repeat the interface of EMPLOYEE.
class EMPLOYEE is
   private attr wage:INT;
   readonly attr name:STR;
   attr id:INT;
   const high_salary:INT := 40000;

   create(a_name:STR, a_id:INT, a_wage:INT):SAME is ...

   highly_paid:BOOL is ...
end;

Routines that are redefined in the child class over-ride the corresponding routines in the included class. For instance suppose we define a new kind of EMPLOYEE - a MANAGER, who has a number of subordinates.
class MANAGER is
   include EMPLOYEE
      create->private oldcreate;
   -- Include employee code and rename create to 'oldcreate'

   readonly attr numsubordinates:INT; -- Public attribute

   create(aname:STR, aid:INT,awage:INT,nsubs:INT):SAME is
      -- Create a new manager with the name 'aname'
      -- and the id 'aid' and number of subordinates = 'nsubs'
      res ::= oldcreate(aname,aid,awage);
      res.numsubordinates := nsubs;
      return res;
   end;
end;

See unnamedlink for the EMPLOYEE definition. The create routine of the MANAGER class extends the EMPLOYEE create routine, which has been renamed to oldcreate (renaming is explained below) and is called by the new create routine.

Points to Note

4.1.1. Renaming

The include clause may selectively rename some of the included features. It is also possible to include a class and make all routines private, or some selectively public
class MANAGER is
   private include EMPLOYEE;
   -- All included features are made private

class MANAGER is
   private include EMPLOYEE  id->id;
   -- Makes the "id" routine public and others stay private

If no clause follows the '->' symbol, then the named features are not included in the class. This is equivalent to 'undefining' the routine or attribute.
class MANAGER is
   include EMPLOYEE id->;  -- Undefine the "id" routine
   attr id:MANAGER_ID;     -- This ' id' has a different type

Points to note

4.1.2. Multiple Inclusion

Sather permits inclusion of code from multiple source classes. The order of inclusion does not matter, but all conflicts between classes must be resolved by renaming. The example below shows a common idiom that is used in create routines to permit an including class to call the attribute initialization routines (by convention, this is frequently called 'init') of parent classes.
class PARENT1 is
   attr a:INT;

   create:SAME is
      return new.init;
   end;

   private init:SAME is
      a := 42;
      return self;
   end;
end;

In the above class, the attributes are initialized in the init routine. The use of such initialization routines is a good practice to avoid the problem of assigning attriutes to the "self" object in the create routine (which is void)

The other parent is similarly defined
class PARENT2 is
   attr c:INT;

   create:SAME is
      return new.init;
   end;

   private init:SAME is
      c := 72;
   end;
end;

In the child class, both parents are initialized by calling the initialization routines in the included classes
class DERIVED is
   include PARENT1 init-> PARENT_init;
   include PARENT2 init-> PARENT2_init; -- Rename init

   attr b:INT;

   create:SAME is
       -- a gets the value 42, b the value 99 and c the value 72
      return new.PARENT1_init.PARENT2_init.init
   end;

   private init:SAME is b := 99; return self;
end; -- class DERIVED

4.1.3. Resolving conflicts

Two methods which are included from different classes may not be able to coexist in the same interface. They are said to conflict with each other. For a full discussion of resolving conflicts, please see unnamedlink. We have to first present the general overloading rule, before discussing when included signatures will conflict and what can then be done about it.

For now, we simply note that if we have signatures with the same name in two included classes, we can simply rename one of them away i.e.
class FOO is
   include BAR bar->;     -- eliminate this 'bar' routine
   include BAR2;          -- Use the 'bar' routine from BAR2