Previous topic  Top  Next topic  Print this Topic
 

Modules

 

In software engineering, modules have been invented to reduce complexity. Closely related and interwoven things are packaged in a common module, whilst loosely coupled things reside in different modules. The communication between modules should be minimal. These principles have been transferred to knowledge bases. Rules and facts describing a closely related part of the domain reside in one module. Thus, an entire knowledge base can be split up into different modules each containing closely related statements about the domain. In some sense this concept is orthogonal to the concept of namespaces. Identifiers with different namespaces may be addressed in one and the same module. On the other hand, identifiers are global across all modules which means that an object with identifier x is the same object in all modules. Thus, modules do not separate objects, but statements about objects. Both, ground statements (statements without variables) and rules and queries are assigned to modules.

Each ObjectLogic file must contain ground statements from exactly one module. The (default) module can be defined at the beginning of the file:

:- module = module1.

 

The name of a module can be an arbitrary ground term, i.e. a constant, a functional term or a namespace term. In the example above we chose a constant. When using a namespace term and an appropriate alias exists, it can also be used for the declaration of the module, e.g.

:- prefix a="http://www.exmple.org/sample#".

:- module = a#sampleModule.

 

The module is assumed to apply for all subsequent ground facts, esp. if they do not declare the module explicitly. The notation for explicitly assigning a module to a ground fact looks like this.

peter:Person@module1.

paul:Person@module1.

bike26:Bike[owner -> paul]@module1.

 

Important note: Since each file can contain only statements from one module, the module references can be omitted without changing the semantics.

Module references are more important within rules and queries. As ground statements, rules are always stored in the defining module, but they can infer statements in any module:

@{ancestorHasFather}

 ?X[hasAncestor ->?Y] :- ?X:Person, ?X[hasFather->?Y]@module2.

 

expresses that the rule named ancestorHasFather resides in sampleModule. All head and body formulas are assuming the same module if the module is not specified explicitly. The above rule, thus, is equivalent to:

@{ancestorHasFather}

 ?X[hasAncestor ->?Y]@a#sampleModule :- ?X:Person@a#sampleModule, ?X[hasFather->?Y]@a#sampleModule.

 

Each literal in a rule body and rule head can use its own module. For body literals this means that the reasoner tries to search for the fact in the mentioned module. For head literals this means, that the new fact is asserted to hold true in its module. A complex example looks like this:

friend(?X,?Y)@module2 :-

 ?X:Person[friend -> ?Y:Person]@module1.

 

This rule expresses that module2 holds the (derived) fact friend(?X,?Y) if it is true in module1 that the ?X and ?Y are persons and related via the friend method. Since module names are terms, it is even possible to use variables as module names in rule bodies.

friend(?X,?Y) :-

 ?X:Person[friend -> ?Y:Person]@?M.

 

This rule searches for statements about friends in every module ?M and asserts a new fact in the default module.

In our previous examples we used only constants for module names. In addition to that, complex module names, i.e. module names consisting of functions are also allowed, for example:

module(Arg1,...,Argn)

 

If Arg1, ... Argn contain variables each binding leads to a separate module name, e.g. module(a,f(b)). It is good practice to use IRI terms as module names, and hence create a universally unique identifier for the modules, for example, with a declaration such as this:

:- prefix a="http://www.exmple.org/sample#".

:- module =a#sample.