next up previous index
Next: Libraries Up: Module System Previous: Creating and Erasing Modules   Index

Subsections

Visibility of Predicates

In ECLiPSe, a predicate can have different levels of visibility. In a given module, a predicate has one and only one of the following visibilities:

local:
a procedure declared as local is only visible from its definition module.

export:
a procedure declared as exported is visible from its definition module and from all modules that import it.

import:
a procedure declared as imported is visible from the module it is imported to. An imported procedure is always equal to the exported procedure it imports (if any).

global:
a procedure declared as global is visible from all modules where no local, exported or imported procedure with the same name is visible. There can be only one global procedure of a given name at any time. Global procedures should be used with care, the normal module interface mechanism is to be preferred.

A procedure whose visibility is not declared is local by default. Therefore, if a predicate shall be visible from outside of its definition module, it must be explicitly declared exported.

Moreover, a procedure may be visible but not defined since it can be declared before it is compiled or asserted.

Access rule for predicates as goal name

Considering the goal Pred(A1, ... AN) invoked from module Caller_module:

If there is a local, exported, imported or global predicate named Pred/N defined in Caller_module, this predicate is the visible one. Else if there is a global predicate declared in any module, this one is visible, else there is no visible predicate Pred/N.

It is also possible to call a non-visible predicate, provided it is global or exported from its definition module. This is done using the :/2 primitive, e.g.

def_module:p(X,Y)

Access rules for predicates as arguments of built-ins

Not all built-ins access their predicate arguments with the same access rule. Some built-ins access the predicate visible from the caller module, others access the predicate defined in the caller module. E.g. spy/1 can be used to set spy points on imported predicates or predicates defined as global in another module than the caller module, whereas abolish/1 can only be used to abolish predicates defined in the caller module or import links (only the link, not the corresponding exported predicate) declared in the caller module. Most of the time, the rule to apply can be found intuitively.

Considering the goal Pred(A1, ..., Pred_arg, ..., AN) called from module Caller_module where Pred_arg specifies a predicate (e.g. like in spy(p/1) or in assert(p(a))). The visibility rule applied for Pred_arg depends on the predicate Pred/N:


Defining and modifying the visibility

There are 5 visibility declaration predicates:

local PredList declares the predicates in PredList (maybe not yet defined) as local in the caller module.

export PredList declares the predicates in PredList (maybe not yet defined) as exported in the caller module.

global PredList declares the predicates in PredList (maybe not yet defined) as global in the caller module.

import PredList from Module declares the predicates in PredList to be imported predicates in the caller module. Each of them are linked to their corresponding exported predicates (maybe not yet) defined in Module.

abolish PredList removes the declaration and the definition of the predicates in Predlist declared or defined in the caller module. As the visibility declaration predicates act on the caller module only, abolishing an imported predicate does not affect the exported predicate itself but only the import link.

The predicate visibility may be changed at any time.

With respect to requirement 2, some visibility changes are restricted: import links (created with import_from/2) must be cut down explicitly (with abolish/1) before defining a new visibility. Vice-versa a local, exported or global declaration or definition must be abolished before an import link is created with import_from/2.

Warnings are raised when redundant declarations occur (e.g. declaring twice the same predicate as local).


Tools

There are predicates in a modular Prolog system that need to work in the space of other modules rather than in the module where they are defined. The most common case is when a predicate is a meta-predicate, i.e. when a predicate has a goal as argument. Other cases are predicates that have other module-dependent arguments (e.g. a record key) or I/O predicates that need to be executed in a certain module context in order to obey the syntax of this module. We call these predicates tool predicates.

Consider the case of a predicate pred/1 which has a goal argument: If the argument goal is called from that predicate, it will be executed in the module space of the definition module of the predicate pred/1 and not in the one of the caller module.

pred(Goal) :-
        ...
        call(Goal), % Goal is called from the definition module of pred/1
... .
When the goal argument of a goal must be used in the module space of the caller of that predicate, we need an additional module argument.
pred(Goal, CallerModule) :-
        ...
        call(Goal)@CallerModule, % Goal is called from CallerModule
... .
To prevent the user having to supply the caller module argument to such predicates (which is likely to cause problem when the predicate that must supply the module argument is recompiled in another module) and to fulfill requirement 3 concerning the privacy (refer to section 9.9), the concept of tool interface has been developed.

The tool interface is a predicate (defined with tool/2) that is connected to a tool body (whose arity is one more than the arity of its tool interface). Its functionality is to automatically supply the caller module of the interface to the last argument of the body procedure and to call that body procedure. Let us assume we have compiled the following tool in the module eclipse

:- global current_def/1.
:- tool(current_def/1, current_def/2).

current_def(Pred, Module) :-
 % get the predicates visible from Module
current_predicate(Pred)@Module,
 % get the flag of Pred visible from Module
get_flag(Pred, definition_module, Module)@Module.
Using the debugger we can easily see how the tool interface supplies the caller module to its tool body.

[eclipse 2]: trace(current_def(X)).
Start debugging - creep mode
  (1) 0  CALL   current_def(X) (dbg)?- creep % type c
(2) 1  CALL   current_def(X, eclipse) (dbg)?- no debug % type n

X = current_def / 2     More? (;) 

X = current_def / 1     More? (;) 

no (more) solution.
And from a new empty module we have:
[eclipse 3]: module(new_module).
[new_module]: assert(p), trace(current_def(X)).
Start debugging - creep mode
  (1) 0  CALL   current_def(X) (dbg)?- creep
  (2) 1  CALL   current_def(X, new_module) (dbg)?- no debug

X = p / 0     More? (;) 

no (more) solution.
When a call to a tool interface is compiled, an additional module argument is supplied by the compiler. Therefore, the compiler must be informed that a predicate is a tool interface before any call is compiled to it. An error is raised when this rule is not respected.

However, it is sometimes not convenient to have the tool definition before compiling any call to it (e.g. when a tool is actually defined in a library that is not yet compiled). This can be solved by using the declaration predicate tool/1. This predicate only informs the system that the predicate specified in its argument is (or will be) a tool interface.

Note that when changing the visibility of a predicate, tools may become visible in modules that already compiled a call to that predicate but not as a tool call. This is for example the case when abolishing a local (non tool) predicate making therefore a global tool visible or when exporting a tool after other modules have imported the predicate. Such visibility changes will raise errors (``inconsistent procedure redefinition'').


System Tools

Many of the system built-in predicates are in fact tools, e.g. read/1, write/1, record/2, compile/1, etc. All predicates which handle modular items must be tools so that they know from which module they have been called. In case that the built-in predicate has to be executed in a different module (this is very often the case when writing user tool predicates), the @/2 construct must be used. It simulates a call of the tool predicate from within a different module context:
current_predicate(P) @ SomeModule


next up previous index
Next: Libraries Up: Module System Previous: Creating and Erasing Modules   Index

1999-08-06