<< 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
- 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
- If
[Z1, Z2, … Zk] = [X1, X2, …, Xn] & [Y1, Y2, … ,Ym], where all Xi are different and all Yi are different, then:
- Z has all mixins of X and Y, but nothing more.
- All Zi are different.
- Sequence Z preserves the partial order defined by sequences X and Y.
- In case of ordering ambiguities, Xi precedes Yj.
- 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 >>