Keris
Home
Quick Tour
   Part 1: Basics
   Part 2: Extensibiliy
   Part 3: Classes


Quick Tour
Part II: Extensibility

This page will introduce features related to extensibility. With these features it is possible to evolve existing modules safely without introducing incompatibilities or inconsistencies.

 3  Refining Modules
Keris has support for non-invasive extensions through a module refinement mechanism. This mechanism allows to refine an existing module by providing new functionality or by overriding existing functionality. The type system guarantees that the refined version of a module is backward compatible to the original version.

We could, for instance, imagine to improve our module MATHS by making the sqrt function more precise (and consequently less efficient). In the example below, we derive a new version of MATHS called NUMATHS which overrides function goodEnought accordingly. Furthermore, module NUMATHS also provides more functionality by offering a new function log.

module NUMATHS refines MATHS {
    private boolean goodEnough(float guess, float x) {
        return abs(sqr(guess) - x) < 0.0001f;
    }
    float log(float x) {
        return (float)Math.log(x);
    }
}

Module refinements like NUMATHS inherit the interface and the member implementations from their parent module and therefore also implement the module interface of their parent module. Furthermore, refinements also inherit the submodules and the context dependencies specified in the parent module.

In Keris, backward compatibility of module refinements implies substitutability; i.e. for the example above, it would be generally legal to replace module MATHS with module NUMATHS. This is exactly what happens in the next program. Here, we refine our module GEOAPP by replacing module MATHS with module NUMATHS. We do this by covariantly overriding the corresponding submodule declaration.

module NUGEOAPP refines GEOAPP {
    module NUMATHS;
}

The new module NUGEOAPP offers the same functionality like GEOAPP but does calculations with a higher precision. Like GEOAPP, it is not executable, since it also depends on an INOUT module. Please note that in the body of NUGEOAPP, we could access the NUMATHS submodule via both module names, MATHS and NUMATHS. The only difference is that when accessed via NUMATHS, we could refer to the new function log.

In a final step we evolve our former MAIN module by incorporating the new NUGEOAPP component. The refined MAIN module is executable, but now uses our newly developed versions of former modules.

module NUMAIN refines MAIN {
    module NUGEOAPP;
}

This small example demonstrates that Keris' module assembly and refinement mechanism supports the extension of atomic modules as well as extensions of fully linked programs (represented by modules with aggregated submodules). Extension of fully linked systems do not require to establish module interconnections again; the fully linked program structure is reused and only the submodules and functions to replace or add have to be specified.

This extensibility mechanism features plug-and-play programming. It does not touch existing code. Therefore, the former module versions do not get lost in the evolution process. We could even assemble a system that makes use of both old and new modules without having to fear unpredictable interferences.

 

 4  Specializing Modules
Refining a module is the process of extending a module by adding new functionality or by modifying existing functionality through overriding. A module refinement yields a new version of an existing module. This new version subsumes the old one in the sense that it is backward compatible to the old version. It is always possible in Keris to replace a module with one of its refinements.

In the following code, module BUGGYMOD aggregates a submodule that subsumes another submodule. In other words, BUGGYMOD defines a context in which two different versions of logically one module are present. Since references to submodule SORTER are ambiguous within module BUGGYMOD, the type system of Keris considers this case to be illegal.

module BUGGYMOD {
    module MATHS;
    module NUMATHS;
}

Sometimes we would like to evolve a module and use the evolved version side by side with the original one. For this case, we need a different form of reuse: We would like to define a new module on top of on an existing one, but we do not want the new module to subsume the old one: it should be logically distinct. We call this process of creating a new distinct module which reuses the definition of an existing module specialization.

Imagine we would like to develop a math module which has similar functionality than NUMATHS, but offers a logarithm function to the base 10 instead of e. Implementing such a module as a refinement would be clearly not desirable due to the change in semantics which could easily break existing code that relies on the contract that NUMATHS.log is the natural logarithm.

module MYMATHS specializes NUMATHS {
    float log(float x) {
        return super.log(x) / super.log(10);
    }
}

Module MYMATHS inherits all members and requirements from NUMATHS and overrides function log. The example shows that in MYMATHS it is still possible to refer to the former implementation via the keyword super.

As a specialization of NUMATHS, module MYMATHS is not required to be backward compatible to its parent module. In particular, it neither subsumes module NUMATHS nor implements any of the module interfaces that are implemented by module NUMATHS. This restriction turns NUMATHS and MYMATHS into completely different modules. Consequently, it is perfectly legal to define a module with both a NUMATHS and a MYMATHS submodule.

module WELLFORMED {
    module NUMATHS;
    module MYMATHS;
}

Often, mutual referential modules have to be specialized at the same time consistently. The ability to refer to a specialized version of a module requires that we are able to specialize context dependencies as well. This rewiring is expressed in the following code using the as operator.

module MYGEO specializes GEO requires MYMATHS as MATHS {
    float one() {
        return MYMATHS.log(10);
    }
}

The MYGEO module specializes module GEO and instead of requiring the original MATHS module, it now refers to the specialized version MYMATHS. While module refinements promote the substitutability of modules, module specializations support the notion of conceptual abstraction on the module level. Conceptual abstraction refers to the ability to factor out code and structure shared by several modules into a common parent module which gets specialized independently into different directions. The specializations represent distinct modules that cannot be substituted for the common parent module.

[Part I] [Part III]