The ECLiPSe module system controls the visibility of predicate names, syntax settings (structures, operators, options, macros), and non-logical store names (records, global variables). Predicates and syntax items can be declared local or they can be exported and imported. Store names are always local.
A source file can be turned into a module by starting it with a module directive. A simple module is:
:- module(greeting). :- export hello/0. hello :- who(X), printf("Hello %w!%n", [X]). who(world). who(friend). |
This is a module which contains two predicates. One of them, hello/0 is exported and can be used by other modules. The other, who/1 is local and not accessible outside the module.
There are 3 ways to use hello/0 from another module. The first possibility is to import the whole ”greeting” module. This makes everything available that is exported from ”greeting”:
:- module(main). :- import greeting. main :- hello. |
The second possibility is to selectively only import the hello/0 predicate:
:- module(main). :- import hello/0 from greeting. main :- hello. |
The third way is not to import, but to module-qualify the call to hello/0:
:- module(main). main :- greeting:hello. |
The module-qualification using :/2
is also used to resolve
name conflicts,
i.e. in the case where a predicate of the same name is defined
in more than one imported module.
In this case, none of the conflicting
predicates is imported - an attempt to call the unqualified predicate
raises an error.
The solution is to qualify every reference with the module name:
:- lib(ic). % exports $>= / 2 :- lib(eplex). % exports $>= / 2 ..., ic:(X $>= Y), ... ..., eplex:(X $>= Y), ...
A more unusual feature, which is however very appropriate for constraint programming, is the possibility to call several versions of the same predicate by specifying several lookup modules:
..., [ic,eplex]:(X $>= Y), ...
which has exactly the same meaning as
..., ic:(X $>= Y), eplex:(X $>= Y), ...
Note that the modules do not have to be known at compile time, i.e. it is allowed to write code like
after(X, Y, Solver) :- Solver:(X $>= Y).
This is however likely to be less efficient because it prevents compile-time optimizations.
The most commonly exported items, apart from predicates, are structure and operator declarations. This is done as follows:
:- module(data). :- export struct(employee(name,age,salary)). :- export op(500, xfx, reports_to). ... |
Such declarations can only be imported by importing the whole module which exports them, i.e. using import data..