The current structure of the module system has been designed with
some specific organisations for large programs in mind. Many large
programs define a basic library layer on top of which the actual program
itself is defined. The module user
, acting as the default
module for all other modules of the program can be used to distribute
these definitions over all program module without introducing the need
to import this common layer each time explicitly. It can also be used to
redefine built-in predicates if this is required to maintain
compatibility to some other Prolog implementation. Typically, the
loadfile of a large application looks like this:
:- use_module(compatibility). % load XYZ prolog compatibility :- use_module( % load generic parts [ error % errors and warnings , goodies % general goodies (library extensions) , debug % application specific debugging , virtual_machine % virtual machine of application , ... % more generic stuff ]). :- ensure_loaded( [ ... % the application itself ]).
The `use_module' declarations will import the public predicates from
the generic modules into the user
module. The
`ensure_loaded' directive loads the modules that constitute the actual
application. It is assumed these modules import predicates from each
other using
use_module/[1,2]
as far as necessary.
In combination with the object-oriented schema described below it is possible to define a neat modular architecture. The generic code defines general utilities and the message passing predicates (invoke/3 in the example below). The application modules define classes that communicate using the message passing predicates.
Another typical way to use the module system is for defining classes within an object oriented paradigm. The class structure and the methods of a class can be defined in a module and the explicit module-boundary overruling describes in section 5.7.2 can by used by the message passing code to invoke the behaviour. An outline of this mechanism is given below.
% Define class point :- module(point, []). % class point, no exports % name type, default access % value variable(x, integer, 0, both). variable(y, integer, 0, both). % method name predicate name arguments behaviour(mirror, mirror, []). mirror(P) :- fetch(P, x, X), fetch(P, y, Y), store(P, y, X), store(P, x, Y).
The predicates fetch/3 and store/3 are predicates that change instance variables of instances. The figure below indicates how message passing can easily be implemented:
% invoke(+Instance, +Selector, ?ArgumentList) % send a message to an instance invoke(I, S, Args) :- class_of_instance(I, Class), Class:behaviour(S, P, ArgCheck), !, convert_arguments(ArgCheck, Args, ConvArgs), Goal =.. [P|ConvArgs], Class:Goal.
The construct <Module>:<Goal> explicitly calls Goal in module Module. It is discussed in more detail in section 5.7.