Darwin Project

The Darwin Project

Darwin Project

Home
Delegation
Project
Compiler
Downloads
History
Papers
People
Contact

Darwin and Lava: Overview

[Darwin] [Lava: Delegation Consultation Multiplicity] [Benefits] [Implementation]

Aims

The Darwin project aims to improve the foundation of object-oriented systems by bridging the gap between the two families of object-oriented languages known today: class-based and prototype-based ones. We want to step beyond the pro and contra discussion of classes versus prototypes, inheritance versus delegation, and safety versus flexibility by investigating

  • how much of both worlds can be beneficially integrated into one uniform model,
  • how widely-used languages like Java or C++ can be extended towards the integrated model, and
  • how such a model can be implemented efficiently.

Results

The current results of the project are:

 

The Darwin Model

Classes,
Subtyping,
Delegation

The Darwin model describes typed, class-based object-oriented languages extended by static and dynamic object-based inheritance (also known as delegation).

Please make sure you understand what is delegation, before reading further.

From a programmer´s point of view Darwin is interesting because it eases their work and promotes unanticpated reuse. From a theoretical point of view its main achievment is the safe integration of static and dynamic delegation with static typing and subtyping.

The Darwin model is best illustrated by the design of the Lava language. [A better tutorial than the following description is certainly overdue.]

 

The Lava Language

Java plus
dynamic
delegation

Lava is an extension of Java by static and dynamic delegation and consultation.

An overview of its specific syntax and semantics is given in the following, using the example from the annotated strategy pattern description. The class Formatting corresponds to the class Compositor from the original example scenario and the class LineBreaking corresponds to the class Compositor.

Delegation

Syntax

 

In Lava definition of objects that delegate part of their behaviour and state to other objects just requires adding the keyword delegatee to an instance variable definition.

Dynamic
object-based
inheritance

For instance, a class of text formatting objects (Compositor) that uses different line breaking strategies (encapsulated in class LineBreaking and its subclasses) can be written as simple as

public class Formatting {
 
       // delegate line breaking requests
       // to the object referred to by lb;
    mandatory delegatee @LineBreaking lb;
 
       // create object with default strategy
    public Formatting () {
       lb = new SimpleLineBreaking();  
    }
 
       // switch strategy
    public setLBStrategy (@LineBreaking _lb) {
      lb = _lb;
   }
 
    ... local behavior ...
}

The Formatting class may use all methods of the LineBreaking type as if they where locally defined or inherited from a superclass - with the essential difference that it may dynamically switch to a different set of method implementations simply by assigning an object of a different LineBreaking subtype to the lineBreak variable!

The use of the @-prefix to LineBreaking in the declaration "delegatee @LineBreaking lb;" says that all subtypes of LineBreaking that do not delegate to LineBreaking are legal values of lb. This restriction is required to ensure type-safety of dynamic delegation (see Darwin thesis). Read @ as a mnemonic for "atomic": in Darwin, objects that implement a type themselves - without delegating part of the implementation - are said to be "atomic intstances" of that type.

Note that in the current implementation of Lava atomic types are not indicated by prepending @. Instead, the keyword atomic can be added to the corresponding declaration. The line

// syntax from PhD thesis:
mandatory delegatee @LineBreaking lb;

is written as

// implemented syntax:
mandatory delegatee atomic LineBreaking lb;

Overriding

Like in the case of class-based inheritance, the Formatting class can fine tune the "inherited" behaviour via overriding.

For instance, assume that the line breaking algorithm calls the method getStretchability() to determine by how many pixels individual text components can be stretched. Then providing a specialized version of this method might be all that is needed to adapt the inherited behaviour to the current delegator´s needs.

public class Formatting {
 
   mandatory delegatee @LineBreaking lineBreak;  //
   ...
 
   public int[] getStretchability() {
      // By how many pixels can individual
      // text components be stretched
   }
 
   ...
}

Static
object-based
inheritance

Static delegation, that is delegation to one fixed parent object, is always safe, even if the values of the delegation field are unrestricted. For instance, one can use the following Lava idiom to implement an overriding decorator (i.e. one that adds own methods and overrides methods of the decorated type):

public class OverridingDecorator {
 
   mandatory final delegatee Decorated d; 
 
   public Decorator (Decorated _d) {
      d = _d;
   }
 
   ... additional and overriding behaviour ...
}

Note that static object-based inheritance still is more dynamic than class-based inheritance. The exact behaviour of an instance of OverridingDecorator can be determined at run-time, by passing subtypes of Decorated as constructor parameters:

OverridingDecorator od;
...
od = new SubtypeOfDecorator(new SubtypeOfDecorated());

Power ...

Note that achieving the effect of dynamic behavior change (strategy pattern) plus fine-tuning of inherited behaviour by overriding could be awkward or even impossible without delegation.
Thus delegation offers the combined power and flexibility of inheritance and of aggregation-based design patterns, without any of their drawbacks.

... without pain

In particular, delegation-based implementations

  • do not break if parent (delegatee) types are modified. Unlike in manual simulations of delegation, there is no "fragile parent class problem".
  • do not require more manual coding than addition of a keyword to a variable declaration. There are no need for manually written "forwarding methods" or "hooks" in the parent type to enable passing of "this".

Consultation

No
overriding

In certain application scenarios overriding of parent methods might not be needed or even undesirable. In such cases one can use consultation instead of delegation.

For instance, the decorator pattern can be implemented in Lava as

public class PureDecorator {
 
   mandatory consultee Decorated d; 
 
   public Decorator (Decorated _d) {
      d = _d;
   }
 
   ... additional behaviour, no overriding ...
}

Automatic
forwarding

Like in the case of delegation there is no need to write any "forwarding methods" and keep them consistent when the parent interface (e.g. Decorated) changes. The programmer can concentrate on the essence of his work (i.e. implementation of the additional behaviour that motivated the use of the decorator pattern).

Multiplicity

Lava has seen versions with and without multiple delegation.

Renaming

 

 

In the initial design and implementation (see Lava 0.5) an object could have multiple delegatees or consultants. Delegation and consultation could even be used jointly by the same object. Ambiguities had to be resolved by Eiffel-like renaming. However, renaming is no general solution to name clashes. Additional, intricate problems arise in the case of "diamond delegation" (Eiffel users would say "repeated delegation"). They require complex run-time bookkeeping that complicate the semantics and implementation.

No
multiplicity

Therefore, the new design of Lava eliminated multiple delegation and consultation in favour of simple and consistent semantics. We still consider multiple delegation to be useful in many situations but prefer to wait until we have a better general solution for resolving conflicts among semantically incompatible methods. This is a topic of onging and future work.

 

Benefits

Flexibility

 

The integration of delegation into mainstream object-oriented languages offers an easy way

  • to make an object appear to be part of and act on behalf of various other ones,
  • to make objects appear to change their behaviour at run-time,
  • to extend and modify existing objects in unanticipated ways,
  • to have mixin inheritance.

Ease of
(re)use

 

Compared to design patterns based only on inheritance and aggregation delegation is easy and results in more reusable designs because

  • it requires minimal coding effort (addition of a keyword to a variable)
  • it introduces no dependencies between "parent" and "child" classes, allowing
    • parent classes to be reused in unanticipated ways,
    • child classes to adapt themselves automatically to extensions of parent types (no fragile parent class problem).

 

Implementation

 

The basic concepts and the implementation scheme for C++, described in Günter's first technical report, were adapted to Java and extended in several ways by Pascal Costanza and Matthias Schickel. In their diploma theses they developed the first proof of concept implementation of Lava as an extension of the Java bytecode compiler and interpreter of the JDK 1.0.2. for Solaris and Windows 32. This implementation was not portable (Lava programs could be executed only on our modified JVM) and its performance was JDK-1.0.2-like ;)

Therefore a translation scheme of to standard Java bytecode was developed, which ensures that Lava programs run on any correct JVM implementation and can take advantage of the sophisticated optimizations performed by current HotSpot JVMs. Thanks to the contribution of Uwe Bardey, Tobias Windeln, and Jörg Gonska, its implementation, the lavac compiler, is available for download and is still progressing further.

If you are interested in details of any of the different implementation schemes, you can find the documents mentioned above on our papers page.


Home
Delegation
Project
Compiler
Downloads
History
Papers
People
Contact

© 1997-2002 Günter Kniesel Date: 01.08.2005