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:
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.
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)
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:
Those predicates will raise errors when used on imported predicates (since imported predicates are always defined as exported in an other module). The import link must be cut explicitly with abolish/1.
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).
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'').
current_predicate(P) @ SomeModule