Keris
| Home Quick Tour Part 1: Basics Part 2: Extensibiliy Part 3: Class Abstractions Archive
| Quick Tour |
Part III: Class Abstractions |
Parts I and II of this tour only considered functions as members of modules. Of course, it is also possible to define other module members such as variables and classes. This final part discusses briefly the significance and use of classes in Keris.
5 Class Interfaces and Implementations
| The essence of most module systems consists in a separation between interfaces and implementations to enable true modularity in combination with separate compilation. Keris uses this principle as an abstraction mechanism even for classes. It destinguishes between class interfaces, class implementations and class fields, which associate the two. The following program defines an interface for modules that provide a `Point` class abstraction. It defines an interface `IPoint` which specifies a signature consisting of two methods `getX()` and `getY()`, and an object constructor. The second declaration states that modules implementing `POINTS` also have to define a class field which implements this interface.
    module interface POINTS {
        interface IPoint {
            IPoint(int x, int y);
            int getX();
            int getY();
        }
        class Point implements IPoint;
    }
We now look at a concrete implementation of `POINTS`. Module `MYPOINTS` below defines a class implementation `CPoint` that implements `IPoint` (which gets inherited from the implemented module interface `POINTS`). Furthermore it defines class field `Point` by separately specifying its interface and implementation: The interface is given by `IPoint` whereas `CPoint` acts as the concrete implementation (or the runtime representation of class field `Point`).
    module MYPOINTS implements POINTS requires INOUT {
        class CPoint implements IPoint {
            int x, y;
            public CPoint(int x, int y) {
                this.x = x;
                this.y = y;
            }
            public int getX() {
                return x;
            }
            public int getY() {
                return y;
            }
        }
        class Point implements IPoint = CPoint;
        Point root() {
            return new Point(0, 0);
        }
        void print(Point p) {
            INOUT.write("(" + p.getX() + ", " + p.getY() + ")");
        }
    }
The implementations of the functions `print` and `root` show that class fields behave just like regular Java classes: They define new types, they can be instantiated, and members of corresponding objects can be accessed. The main difference to regular Java classes is that class fields are virtual and therefore can be covariantly overridden in refined modules. Covariant overriding of class fields includes the extension of the set of implemented interfaces as well as the ability to specify a new class field implementation. Here is another module interface that describes a possible color aspect of points. Please note that this new module interface `COLORPOINTS` is not at all related to `POINTS`. It simply also specifies a class field `Point`, which is, this time, constrained by interface `IColor`.
    module interface COLORPOINTS requires COLOR {
        interface IColor {
            void setColor(COLOR.Color color);
            COLOR.Color getColor();
        }
        class Point implements IColor;
    }
We now refine the original MYPOINTS module by additionally implementing the new `COLORPOINTS` module interface. Please note that `MYCOLORPOINTS` implicitly requires module `COLOR` since its implemented module interface `MYPOINTS` does so.
    module MYCOLORPOINTS refines MYPOINTS implements COLORPOINTS {
        import COLOR.*;
        class CCPoint extends CPoint implements IColor {
            Color color;
            public CCPoint(int x, int y) {
                super(x, y);
            }
            public void setColor(Color color) {
                this.color = color;
            }
            public Color getColor() {
                return color;
            }
        }
        class Point implements IPoint, IColor = CCPoint;
        void print(Point p) {
            super.print(p);
            INOUT.write(" col = " + p.getColor());
        }
    }
Refinement `COLORPOINTS` specifies that class field `Point` now also supports the `IColor` interface and is implemented by the `CCPoint` class. Furthermore, `print` is overridden to include the color in the output. At this point, one might wonder what happens to method `root` of the original module `POINTS` which instantiates class field `Point`. In fact, for the refined module it now returns a colored point since we were overriding class field `Point`. The ability to covariantly refine types (or class fields in our case) is essential for extending object-oriented software. Most object-oriented languages support interface and implementation inheritance. But inheritance alone does not support software refinement or software specialization well. Existing code refers to the former type and often cannot be overridden covariantly in a type-safe way to allow access to extended features.
6 Class Dependencies
| The previous section explained how to declare and how to evolve class abstractions. The presented mechanism does not allow to relate different class fields to each other; every class field defines an own independent interface/implementation pair. We need a mechanism similar to subclassing/subtyping that introduces dependencies between class fields. Since implementation inheritance is difficult to handle modularly (with full abstraction), Keris only supports the explicit declaration of subtype relationships between class fields. The following code illustrates this.
    module SHAPES requires POINTS {
        interface IShape {
            boolean inShape(POINTS.Point pt);
        }
        abstract class Shape implements IShape;
        private java.util.Set shapes = new java.util.HashSet();
        void registerShape(Shape s) {
            shapes.add(s);
        }
    }
    module BOXES requires SHAPES, POINTS {
        import POINTS.*;
        import SHAPES.*;
        interface IBox {
            IBox(Point topleft, Point botright);
        }
        class Box extends Shape implements IShape, IBox = {
            Point topleft, botright;
            public Box(Point topl, Point botr) {
                topleft = topl;
                botright = botr;
                SHAPES.registerShape(this);
            }
            public boolean inShape(Point pt) {
                return pt.getX() > topleft.getX() &&
                       pt.getX() < botright.getX() &&
                       pt.getY() > topleft.getY() &&
                       pt.getY() < botright.getY();
            }
        }
    }
In this program, we define an abstract class field `Shape` within module `SHAPES`. Abstract class fields are like abstract classes: They simply define types, and cannot be instantiated. Thus, there is no need to specify implementations for abstract class fields. Module `BOXES` defines a class field `Box` which extends `SHAPES.Shape`. This extends declaration defines `Box` to denote a subtype of `Shape` requiring that `Box` implements at least all the interfaces implemented by `Shape`. The type checker has to make sure that it is not possible to link refinements of `BOXES` and `SHAPES` where this invariant is broken. Thus, such subtype dependencies between class fields promote the consistent refinement or specialization of class field hierarchies. Here is an example which successfully links modules `BOXES` and `SHAPES` in the context of module `LEGAL`.
    module LEGAL requires POINTS {
        module BOXES;
        module SHAPES;
    }
The following "refinement" of module `LEGAL` is not well-formed, because it breaks the dependency established between the class fields `Shape` and `Box`.
    module NUSHAPES refines SHAPES {
        interface INuShape {
            void scale(int factor);
        }
        abstract class Shape implements IShape, INuShape;
    }
    module ILLLEGAL refines LEGAL {
        module NUSHAPES;
    }
[Part I] [Part II] |