The module system

Author(s): Daniel Cabeza, The Ciao Development Team.

Modularity is a basic notion in a modern computer language. Modules allow dividing programs into several parts, which have their own independent name spaces. Each module is written in its own file (see module/2 and module/3) and consists of a sequence of directives and predicate definitions.

Modules provide functionality to other modules by exporting some of the predicates defined inside the module (and also through multifile predicates). However, a module does not modify the syntax that can be used in another module that loads it. This is done instead through the mechanism of packages (see Packages and language extension).

See [CH00a] for a detailed description of the Ciao module system.

Visibility rules

The module system in Ciao is, as in most Prolog implementations, procedure based. This means that predicate names are local to a module, but functor/atom names in data are shared (at least by default).

The predicates visible in a module are the predicates defined in that module, plus the predicates imported from other modules. Only predicates exported by a module can be imported from other modules. The default module of a given predicate name is the local one if the predicate is defined locally, else the last module from which the predicate is imported, where explicit imports have priority over implicit ones (that is, a predicate imported through a use_module/2 declaration is always preferred over a predicate imported through a use_module/1 declaration). To refer to a predicate from a module which is not the default module for that predicate the name has to be module qualified. A module-qualified predicate name has the form Module:Predicate as in the call debugger:debug_module(M). Note that in Ciao this module qualification cannot be used for gaining access to predicates that have not been imported, nor for defining clauses of other modules.

Files with no module declaration ('user' files)

All predicates defined in files with no module declaration belong to a special module called user, from which they are all implicitly exported. This provides backward compatibility for programs written for Prolog implementations with no module system and allows dividing programs into several files without being aware of the module system at all. Note that this feature is only supported for the above-mentioned backward-compatibility reasons, and the use of user files is discouraged. Many attractive compilation features of Ciao cannot be supported for user modules.

Multifile predicates

The case of multifile predicates (defined with the declaration multifile/1) is also special. Multifile predicates can be defined by clauses distributed in several modules, and all modules which define a predicate as multifile can use that predicate. The name space of multifile predicates is independent, as if they belonged to the special module multifile.

Basic directives

Unlike in other Prolog systems, directives in Ciao are not goals to be executed by the compiler or top level. Instead, they are read and acted upon by these programs. The advantage of this is that the effect of the directives is consistent for executables, code loaded in the top level, code analyzed by the preprocessor, etc.

As a result, by default only the builtin directives or declarations defined in this section are available in user programs. However, it is possible to define new declarations using the new_declaration/1 and new_declaration/2 directives (or using packages including them). Also, packages may define new directives via code translations.

Libraries imported by default ('builtins')

While in Ciao there are no 'built-in' predicates (i.e., predicates whose load cannot be avoided or that that cannot be redefined --see below) for convenience every module or user file imports implicitly a number of modules called builtin modules (also referred to as default modules). Which exact modules are imported by default is controlled by the third argument of module/3 declarations, the lack thereof in module/2 declarations, some rules for user files, etc., as described below. For example, for backward compatibility with traditional Prolog systems, if a module/2 declaration is used, then the traditional predicates that are built-in in most Prolog systems are imported in that module (see package classic, Classic Prolog).

Predicates coming from builtin/default modules are imported before all other importations of the module. This allows the redefinition of builtins, i.e., the redefinition of any of the predicates imported by default from builtin/default modules (with the exception of true/0) by either defining local versions of these predicates or by importing them from other modules.

Moreover, the implicit importation of the basic modules can be fully disabled by some special packages (for example, omitting all default imports with noprelude, or defining pure Prolog modules with the pure package).


Usage and interface

  • Library usage:
    Modules are an intrinsic feature of Ciao, so nothing special has to be done to use them.
  • Exports:

Documentation on exports

A module name is an atom, not containing characters `:' or `$'. Also, user and multifile are reserved, as well as the module names of all builtin modules (because in an executable all modules must have distinct names).

Usage:modulename(M)

M is a module name (an atom).

    Documentation on internals

    DECLARATIONmodule/3

    Usage::- module(Name,Exports,Packages).

    Declares a module of name Name which exports the predicates in Exports, and uses the packages in Packages. Name must match the name of the file where the module resides, without extension.

    For each source in Packages, the corresponding file, which must be a package is used. If the source is specified with a path alias, this is the file included; if it is an atom, the library paths are searched. See package/1, Packages and language extension for a description of package files. If Packages is [] no packages are loaded.

    The module directive must appear first in the file.

    As a special case Exports can be _ which means that all predicates in the module are exported. Also, Name can be _ which means that the name of the module is the name of the file. Thus, the following declaration at the beginning of a file:

    :- module(_,_,[]).

    takes the module name from the file name, exports all predicates defined, and does itself not load any packages.

    Also, if the compiler finds an unknown declaration as the first term in a file, the name of the declaration is considered as a package library to be included, and the arguments of the declaration (if present) are interpreted like the arguments of module/3.

    DECLARATIONmodule/2

    Usage::- module(Name,Exports).

    Same as directive module/3, but loads implicitly the classic package. This default package provides all the standard features provided by most Prolog systems so that Prolog programs with traditional module/2 declarations can run without any change. See Classic Prolog.

    DECLARATIONuse_package/1
    :- use_package(Package).

    Specifies the use in this file of the packages defined in Package. See the description of the third argument of module/3 for an explanation of package files.

    This directive must appear the first in the file, or just after a module/3 declaration.

    A file with no module declaration, and with no use_package directives, uses implicitly the classic package (see Classic Prolog). However, if at least one use_package directive is present, then classic is not loaded. If needed, must be loaded via use_package, together with any other packages required.

    Usage 1::- use_package(Package).

    Usage 2::- use_package(Package).

    • The following properties should hold at call time:
      (basic_props:list/2)Package is a list of sourcenames.

    DECLARATIONuse_module/2

    Usage::- use_module(Module,Imports).

    Specifies that this code imports from the module defined in Module the predicates in Imports. The imported predicates must be exported by the other module.

    DECLARATIONuse_module/1

    Usage::- use_module(Module).

    Specifies that this code imports from the module defined in Module all the predicates exported by it. The previous version with the explicit import list is preferred to this as it minimizes the chances to have to recompile this code if the other module changes.

    DECLARATIONimport/2

    Usage::- import(Module,Imports).

    Declares that this code imports from the module with name Module the predicates in Imports.

    Important note: this declaration is intended to be used when the current module or the imported module is going to be dynamically loaded, and so the compiler does not include the code of the imported module in the current executable (if only because the compiler cannot know the location of the module file at the time of compilation). For the same reason the predicates imported are not checked to be exported by Module. Its use in other cases is strongly discouraged, as it disallows many compiler optimizations.

    This is an example of such a case for a dynamically loaded module:

    :- module(_,_).
    
    :- import(bar,[b/1]).
    
    main(X) :- 
         use_module(bar),
         b(X).

    DECLARATIONreexport/2

    Usage::- reexport(Module,Preds).

    Specifies that this code reexports from the module defined in Module the predicates in Preds. This implies that this module imports from the module defined in Module the predicates in Preds, an also that this module exports the predicates in Preds .

    DECLARATIONreexport/1

    Usage::- reexport(Module).

    Specifies that this code reexports from the module defined in Module all the predicates exported by it. This implies that this module imports from the module defined in Module all the predicates exported by it, an also that this module exports all such predicates .

    DECLARATIONensure_loaded/1

    Usage:ISO:- ensure_loaded(File).

    Specifies that the code present in File will be included in the executable being prepared, in the user module. The file File cannot have a module declaration. This directive is intended to be used by programs not divided in modules. Dividing programs into modules is however strongly encouraged, since most of the attractive features of Ciao (such as static debugging and global optimization) are only partially available for user modules.

    DECLARATIONinclude/1

    Usage:ISO:- include(File).

    The contents of the file File are included in the current program text exactly as if they had been written in place of this directive.

    DECLARATIONexport/1

    Usage 1::- export(Pred).

    Adds Pred to the set of exported predicates.

    Usage 2::- export(Exports).

    Adds Exports to the set of exported predicates.

    • The following properties should hold at call time:
      (basic_props:list/2)Exports is a list of prednames.

    DECLARATIONmultifile/1

    Usage:ISO:- multifile Predicates.

    Specifies that each predicate in Predicates may have clauses in more than one file. Each file that contains clauses for a multifile predicate must contain a directive multifile for the predicate. The directive should precede all clauses of the affected predicates, and also dynamic/data declarations for the predicate. This directive is defined as a prefix operator in the compiler.

    DECLARATIONmeta_predicate/1

    Usage::- meta_predicate MetaSpecs.

    Specifies that the predicates in MetaSpecs have arguments which have to be module expanded (predicates, goals, etc). meta_predicate/1 directives are only mandatory for exported predicates (in modules). This directive is defined as a prefix operator in the compiler.

    • The following properties should hold at call time:
      (basic_props:sequence/2)MetaSpecs is a sequence of metaspecs.

    DECLARATIONredefining/1

    Usage::- redefining(Predicate).

    Specifies that this module redefines predicate Predicate, also imported from other module, or imports it from more than one module. This prevents the compiler giving warnings about redefinitions of that predicate. Predicate can be partially (or totally) uninstantiated, to allow disabling those warnings for several (or all) predicates at once.

    • The following properties should hold at call time:
      (basic_props:compat/2)predname is compatible with Predicate.

    DECLARATIONdiscontiguous/1

    Usage:ISO:- discontiguous Predicates.

    Specifies that each predicate in Predicates may be defined in this file by clauses which are not in consecutive order. Otherwise, a warning is signaled by the compiler when clauses of a predicate are not consecutive (this behavior is controllable by the prolog flag discontiguous_warnings). The directive should precede all clauses of the affected predicates. This directive is defined as a prefix operator in the compiler.

    DECLARATIONimpl_defined/1

    Usage::- impl_defined(Predicates).

    Specifies that each predicate in Predicates is implicitly defined in the current prolog source, either because it is a builtin predicate or because it is defined in a C file. Otherwise, a warning is signaled by the compiler when an exported predicate is not defined in the module or imported from other module.

    REGTYPEmetaspec/1
    A meta-predicate specification for a predicate is the functor of that predicate applied to terms which represent the kind of module expansion that should be applied to each argument. Possible contents are represented as:

    ?,+,-,_
    These values denote that this argument is not module expanded.

    goal
    This argument will be a term denoting a goal (either a simple or complex one) which will be called. For commpatibility reasons it can be named as : as well.

    clause
    This argument will be a term denoting a clause.

    fact
    This argument should be instantiated to a term denoting a fact (head-only clause).

    spec
    This argument should be instantiated to a predicate name, as Functor/Arity.

    pred(N)
    This argument should be instantiated to a predicate construct to be called by means of a call/N predicate call (see call/2).

    list(Meta)
    This argument should be instantiated to a list of terms as described by Meta (e.g. list(goal)).

    addterm(Meta)
    This argument should be instantiated to the meta-data specified by Meta, and an argument added after this one will carry the original data without module expansion. Not intended to be used by normal users.

    addmodule(Meta)
    This argument should be instantiated to the meta-data specified by Meta, and in an argument added after this one will be passed the calling module, for example to allow handling more involved meta-data by using conversion builtins. addmodule is an alias of addmodule(?). Not intended to be used by normal users.

    Usage:metaspec(M)

    M is a meta-predicate specification.

      DECLARATIONinitialization/1

      Usage:ISO:- initialization(Goal).

      Goal will be executed at the start of the execution of any program containing the current code. The initialization of a module/file never runs before the initializations of the modules from which the module/file imports (excluding circular dependencies).

      • The following properties should hold at call time:
        (basic_props:cgoal/1)Goal is a term which represents a goal, i.e., an atom or a structure.

      DECLARATIONon_abort/1

      Usage::- on_abort(Goal).

      Goal will be executed after an abort of the execution of any program containing the current code.

      • The following properties should hold at call time:
        (basic_props:cgoal/1)Goal is a term which represents a goal, i.e., an atom or a structure.

      Documentation on imports

      This module has the following direct dependencies: