Home

Documentation

* Programming Guide * CJDT User Guide * Publications * Tutorial * Language Spec.

Examples

Downloads

Source Code

FAQ

Community Info

About Us

 

 
Search:
 
ST Logo
TUD Logo
<< Overview | Contents | Virtual Classes >>

Mixin Composition

Mixins

A mixin is a class with parameterizable superclass. We denote mixin class as M[T]. M is the mixin name and T is the superclass parameter. Mixins can be composed by passing one of them as superclass parameter to another one:
 
M1[T1] & M2[T2] = M1[M2[T2]]

The super class parameter of the first mixin is bound to M2. The superclass of the composed mixin is further parameterizable by T2.

Let's imagine we have an abstract Shape class, which stores shape position and defines draw() method, which should be redefined for specific shapes. Then we also implement TextShape, which displays some text:
 
cclass Shape {
   protected int posx, posy;
   public void draw(Graphics g) {
   }
}
cclass TextShape extends Shape {
   protected String text;
   public void draw(Graphics g) {
      super.draw(g);
      g.drawText(posx, posy, text); 
   }
}

It may be desirable to decorate different Shape subclasses, such as Ellipse or Polygon, with TextShape functionality. It means we want want to view TextShape as delta, which adds text drawing functionality to any class. Or in other words TextShape should be considered as mixin, which can be combined with any subclass of Shape. Let’s imagine that we want to combine TextShape with following Rectangle class:
  
cclass Rectangle extends Shape {
   protected int x2, y2;
   public void draw(Graphics g) {
      super.draw(g);
      g.drawRectangle(posx, posy, x2, y2);      
   }
}

All Caesar classes can be viewed as mixins, which can be combined with any subclass of the declared parent class. In our case we have mixins TextShape[T1] and Rectangle[T2], where T1 and T2 must be subclasses of Shape. The combination TextShape[T] & Rectangle[T] = TextShape[Rectangle[T]] produces TextShape, which parent is replaced with Rectangle. In Caesar this can be expressed in following way:
 
cclass RectangleText extends TextShape & Rectangle {   
}

As a result we obtain functionality equivalent to following inheritance hierarchy:


We can see that RectangleText inherits the draw() method from TextShape, but super call in draw() now refers not to draw() in Shape, but to draw() in Rectangle. Therefore, RectangleText will be displayed as a rectangle with a text.

From type system perspective RectangleText is a subtype of both TextShape and Rectangle and can be used polymorphicaly instead of any of these classes.

If we use TextShape as mixin, it does not prevent from instantiating TextShape as a normal class as well. In this case it simply receives its declared parent Shape as default mixin parameter, i.e. TextShape as a normal class is actually TextShape[Shape]. Basically we can view each class declaration as a mixin and a class as a combination of mixins. Therefore, we can represent each class by its mixin list. For example:
 
TextShape = [TextShape, Shape]
Rectangle = [Rectangle, Shape]
RectangleText = [RectangleText, TextShape, Rectangle, Shape]
 

If we view mixins as plain Java classes, and organize them in the inheritance hierarchy according the order in the mixin list, we will get the same functionality. The difference between mixins and plain Java classes is however that plain Java classes have fixed parents and cannot be reorganized to different inheritance hierarchies.

Mixin Linearization

While combination of two simple mixins is more or less intuitive, combination of two classes, which are already defined as combination of multiple mixins may be not so obvious. If we view each class as a list of mixins then mixin combination can be defined as an operation with such lists. The combination must again produce a list, therefore this process is called mixin linearization. We have seen that:
 
TextShape & Rectangle = 
    [TextShape, Shape] & [Rectangle, Shape] = 
    [TextShape, Rectangle, Shape]

This is precisely what we need in this case. We want that both rectangle and text are drawn at the same position defined for the shape, so the Shape mixin must be shared. It also must be above TextShape and Rectangle in the equivalent inheritance hierarchy, because both TextShape and Rectangle use Shape functionality. In fact mixin combination A & B should follow following rules:

Rule1. A & B must contain all mixins from A and from B.

Rule2. A & B should not have duplicate mixins.

Rule3. A & B must preserve the order of mixins as they appear in A and in B.

Rule4. If order is not constrained by Rule3, a mixin of A should appear before a mixin of B.

In our case Rule1 tells that combination of TextShape and Rectangle has only single copies of Rectangle, TextShape and Shape mixins. Rule2 tells that Shape must appear only once even if it is mentioned in both initial lists. Rule3 tells that TextShape and Rectangle should appear before Shape. Rule4 tells that TextShape should appear before Rectangle.

From these rules we can observe, that if mixin lists have contradicting mixin order, they cannot be combined.

Let’s see how these rules work with a more complicated example:
 
cclass ColoredShape extends Shape {
   protected Color col;
   public void draw(Graphics g) {
      g.setColor(col);
      super.draw(g); 
   }
}
cclass ColoredRectangle extends ColoredShape & Rectangle   
}
cclass ColoredText extends ColoredShape & TextShape   
}
cclass ColoredRectangleText extends ColoredText & ColoredRectangle
}

ColoredShape sets color and calls draw() of its parent. We define classes ColoredRectangle and ColoredText as ColoredShape[Rectangle] and ColoredShape[TextShape] to get colored versions of corresponding shapes. It is not obvious however whether the further combination of these classes produces what we expect: colored rectangle with text.

In order to understand what happens let’s calculate the mixin list of ColoredRectangleText according our rules. For the sake of simplicity we will ignore the empty mixins ColoredRectangle, ColoredText and ColoredRectangleText:
 
ColoredRectangleText = 
    ColoredText & ColoredRectangle = 
    (ColoredShape & TextShape) & (ColoredShape & Rectangle) =
    ([ColoredShape, Shape] & [TextShape, Shape]) & 
        ([ColoredShape, Shape] & [Rectangle, Shape]) =
    [ColoredShape, TextShape, Shape] & [ColoredShape, Rectangle, Shape] =
    [ColoredShape, TextShape, Rectangle, Shape]

We see that combination of non-empty mixins of ColoredRectangleText is equivalent to following inheritance sequence:


This is exactly what we need: color is set before drawing text and rectangle.

Summary of Rules

  1. If class is declared as “cclass A extends P1 & P2 & … & Pn” then the mixin list of class A evaluates to:
    A = [A] & P1 & P2 & … & Pn
  2. If [Z1, Z2, … Zk] = [X1, X2, …, Xn] & [Y1, Y2, … ,Ym], where all Xi are different and all Yi are different, then:
    1. Z has all mixins of X and Y, but nothing more.
    2. All Zi are different.
    3. Sequence Z preserves the partial order defined by sequences X and Y.
    4. In case of ordering ambiguities, Xi precedes Yj.
  3. Operational semantics of class defined by mixin list [X1, X2, ... Xn] is equivalent to the semantics of class X1 in the imaginary inheritance hierarchy, where Xi is a subclass of Xi+1

<< Overview | Contents | Virtual Classes >>