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.