As indicated in the introduction, the problem with a predicate based module system lies in the difficulty to find the correct predicate from a Prolog term. The predicate `solution(Solution)' can exist in more than one module, but `assert(solution(4))' in some module is supposed to refer to the correct version of solution/1.
Various approaches are possible to solve this problem. One is to add an extra argument to all predicates (e.g. `assert(Module, Term)'). Another is to tag the term somehow to indicate which module is desired (e.g. `assert(Module:Term)'). Both approaches are not very attractive as they make the user responsible for choosing the correct module, inviting unclear programming by asserting in other modules. The predicate assert/1 is supposed to assert in the module it is called from and should do so without being told explicitly. For this reason, the notion context module has been introduced.
Each predicate of the program is assigned a module, called it's definition module. The definition module of a predicate is always the module in which the predicate was originally defined. Each active goal in the Prolog system has a context module assigned to it.
The context module is used to find predicates from a Prolog term. By
default, this module is the definition module of the predicate running
the goal. For meta-predicates however, this is the context module of the
goal that invoked them. We call this module_transparent in
SWI-Prolog. In the `using maplist' example above, the predicate
maplist/3
is declared module_transparent. This implies the context module remains extend
,
the context module of add_extension/3. This way maplist/3
can decide to call extend_atom in module
extend
rather than in it's own definition module.
All built-in predicates that refer to predicates via a Prolog term are declared module_transparent. Below is the code defining maplist.
:- module(maplist, maplist/3). :- module_transparent maplist/3. % maplist(+Goal, +List1, ?List2) % True if Goal can successfully be applied to all successive pairs % of elements of List1 and List2. maplist(_, [], []). maplist(Goal, [Elem1|Tail1], [Elem2|Tail2]) :- apply(Goal, [Elem1, Elem2]), maplist(Goal, Tail1, Tail2). |
The mechanism above is sufficient to create an acceptable module system. There are however cases in which we would like to be able to overrule this schema and explicitly call a predicate in some module or assert explicitly in some module. The first is useful to invoke goals in some module from the user's toplevel or to implement a object-oriented system (see above). The latter is useful to create and modify dynamic modules (see section 4.7).
For this purpose, the reserved term :/2 has been introduced. All
built-in predicates that transform a term into a predicate reference
will check whether this term is of the form `<Module>:<Term>'
. If so, the predicate is searched for in Module instead of
the goal's context module. The
:
operator may be nested, in which case
the inner-most module is used.
The special calling construct <Module>:<Goal> pretends Goal is called from Module instead of the context module. Examples:
?- assert(world:done). % asserts done/0 into module world ?- world:assert(done). % the same ?- world:done. % calls done/0 in module world |