module fixes plus add better docs

This commit is contained in:
Vítor Santos Costa 2014-07-17 12:19:38 -05:00
parent dbff20343a
commit ce8a4b6958
8 changed files with 667 additions and 471 deletions

View File

@ -542,12 +542,6 @@ init_between( USES_REGS1 )
return cont_between( PASS_REGS1 );
}
/**
*
* @}
*
* @}
*/
void
Yap_InitEval(void)
{
@ -562,3 +556,7 @@ Yap_InitEval(void)
Yap_InitCPredBack("between", 3, 2, init_between, cont_between, 0);
}
/**
*
* @}
*/

View File

@ -776,11 +776,13 @@ WARN_LOGFILE =
# spaces.
# Note: If this tag is empty the current directory is searched.
INPUT = /Users/vsc/git/yap-6.3/pl/modules.yap
# INPUT = /Users/vsc/git/yap-6.3/pl/absf.yap
# INPUT = /Users/vsc/git/yap-6.3/packages/ProbLog/problog_learning_lbdd.yap
# INPUT = /Users/vsc/git/yap-6.3/packages/cplint/mcintyre.pl
#INPUT = /Users/vsc/git/yap-6.3/packages/R/R.pl
INPUT = docs/yap.md \
INPUTX = docs/yap.md \
pl \
C \
H \
@ -868,7 +870,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE = *pltotex.pl
EXCLUDE = *pltotex.pl packages/swig/android
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded

View File

@ -11,7 +11,9 @@ This file documents the YAP Prolog System version 6.3.4, a high-performance Prol
+ @subpage Loading_Programs presents the main predicates and
directives available to load files and to control the Prolog environment.
+ @subpage abs_file_name explains how to find a file full path.
+ @ref yapmodules introduces the YAP module system and meta-predicates.
+ @ref absf0 explains how to find a file full path.
+ @subpage BuilthYins describes predicates providing core YAP
functionality. Examples include
@ -1510,7 +1512,7 @@ if it is `source` clauses are compiled and source code is stored;
if it is `assert_all` clauses are asserted into the data-base.
</li>
<li>comnsult(+ _Mode_)
<li>consult(+ _Mode_)
This extension controls the type of file to load. If _Mode_
is `consult`, clauses are added to the data-base,
is `reconsult`, clauses are recompiled,
@ -1986,7 +1988,7 @@ defined, or search the default library directory.
</li>
</ul>
@section Modules The Module System
@section old_Modules The Module System
Module systems are quite important for the development of large
applications. YAP implements a module system compatible with the Quintus
@ -2006,314 +2008,6 @@ YAP allows one to ignore the module system if one does not want to use
it. Last note that using the module system does not introduce any
significant overheads.
@subsection Module_Concepts Module Concepts
The YAP module system applies to predicates. All predicates belong to a
module. System predicates belong to the module `primitives`, and by
default new predicates belong to the module `user`. Predicates from
the module `primitives` are automatically visible to every module.
Every predicate must belong to a module. This module is called its
<em>source module</em>.
By default, the source module for a clause occurring in a source file
with a module declaration is the declared module. For goals typed in
a source file without module declarations, their module is the module
the file is being loaded into. If no module declarations exist, this is
the current <em>type-in module</em>. The default type-in module is
`user`, but one can set the current module by using the built-in
`module/1`.
Note that in this module system one can explicitly specify the source
mode for a clause by prefixing a clause with its module, say:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
user:(a :- b).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In fact, to specify the source module for a clause it is sufficient to
specify the source mode for the clause's head:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
user:a :- b.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rules for goals are similar. If a goal appears in a text file with a
module declaration, the goal's source module is the declared
module. Otherwise, it is the module the file is being loaded into or the
type-in module.
One can override this rule by prefixing a goal with the module it is
supposed to be executed in, say:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
nasa:launch(apollo,13).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
will execute the goal `launch(apollo,13)` as if the current source
module was `nasa`.
Note that this rule breaks encapsulation and should be used with care.
@subsection Defining_Modules Defining a New Module
A new module is defined by a `module` declaration:
<ul>
<li>module(+ _M_,+ _L_) @anchor module
This directive defines the file where it appears as a module file; it
must be the first declaration in the file.
_M_ must be an atom specifying the module name; _L_ must be a list
containing the module's public predicates specification, in the form
`[predicate_name/arity,...]`.
The public predicates of a module file can be made accessible by other
files through the directives [use_module/1](@ref use_module), `use_module/2`,
[ensure_loaded/1](@ref ensure_loaded) and the predicates [consult/1](@ref consult) or
[reconsult/1](@ref reconsult). The non-public predicates
of a module file are not visible by other files; they can, however, be
accessed by prefixing the module name with the
`:/2` operator.
</li>
</ul>
The built-in `module/1` sets the current source module:
<ul>
<li>module(+ _M_,+ _L_, + _Options_)
Similar to [module/2](@ref module), this directive defines the file where it
appears in as a module file; it must be the first declaration in the file.
_M_ must be an atom specifying the module name; _L_ must be a
list containing the module's public predicates specification, in the
form `[predicate_name/arity,...]`.
The last argument _Options_ must be a list of options, which can be:
<ul>
<li>filename
the filename for a module to import into the current module.
</li>
<li>library(file)
a library file to import into the current module.
</li>
<li>hide( _Opt_)
if _Opt_ is `false`, keep source code for current module, if
`true`, disable.
</li>
</ul>
</li>
<li>module(+ _M_)
Defines _M_ to be the current working or type-in module. All files
which are not bound to a module are assumed to belong to the working
module (also referred to as type-in module). To compile a non-module
file into a module which is not the working one, prefix the file name
with the module name, in the form ` _Module_: _File_`, when
loading the file.
</li>
<li>export(+ _PredicateIndicator_) @anchor export
Add predicates to the public list of the context module. This implies
the predicate will be imported into another module if this module is
imported with `use_module/[1,2]`. Note that predicates are normally
exported using the directive [module/2](@ref module). [export/1](@ref export) is meant
to handle export from dynamically created modules. The directive argument
may also be a list of predicates.
</li>
<li>export_list(? _Mod_,? _ListOfPredicateIndicator_) @anchor export_list
The list _ListOfPredicateIndicator_ contains all predicates exported
by module _Mod_.
</li>
</ul>
@subsection Using_Modules Using Modules
By default, all procedures to consult a file will load the modules
defined therein. The two following declarations allow one to import a
module explicitly. They differ on whether one imports all predicate
declared in the module or not.
<ul>
<li>use_module(+ _F_) @anchor use_module
Loads the files specified by _F_, importing all their public
predicates. Predicate name clashes are resolved by asking the user about
importing or not the predicate. A warning is displayed when _F_ is
not a module file.
</li>
<li>use_module(+ _F_,+ _L_)
Loads the files specified by _F_, importing the predicates specified
in the list _L_. Predicate name clashes are resolved by asking the
user about importing or not the predicate. A warning is displayed when
_F_ is not a module file.
</li>
<li>use_module(? _M_,? _F_,+ _L_)
If module _M_ has been defined, import the procedures in _L_ to
the current module. Otherwise, load the files specified by _F_,
importing the predicates specified in the list _L_.
</li>
</ul>
@subsection MetahYPredicates_in_Modules Meta-Predicates and Modules
The module system must know whether predicates operate on goals or
clauses. Otherwise, such predicates would call a goal in the module they
were defined, instead of calling it in the module they are currently
executing. So, for instance, consider a file example.pl:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:- module(example,[a/1]).
a(G) :- call(G)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We import this module with `use_module(example)` into module
`user`. The expected behavior for a goal `a(p)` is to
execute goal `p` within the module `user`. However,
`a/1` will call `p` within module `example`.
The [meta_predicate/1](@ref meta_predicate) declaration informs the system that some
arguments of a predicate are goals, clauses, clauses heads or other
terms related to a module, and that these arguments must be prefixed
with the current source module:
<ul>
<li>meta_predicate _G1_,...., _Gn_ @anchor meta_predicate
Each _Gi_ is a mode specification.
If the argument is `:`, it does not refer directly to a predicate
but must be module expanded. If the argument is an integer, the argument
is a goal or a closure and must be expanded. Otherwise, the argument is
not expanded. Note that the system already includes declarations for all
built-ins.
For example, the declaration for [call/1](@ref call) and [setof/3](@ref setof) are:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:- meta_predicate call(0), setof(?,0,?).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</li>
</ul>
The previous example is expanded to the following code which explains,
why the goal `a(p)` calls `p` in `example` and not in
`user`. The goal `call(G)` is expanded because of the
meta-predicate declaration for [call/1](@ref call).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:- module(example,[a/1]).
a(G) :- call(example:G)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By adding a meta-predicate declaration for `a/1`, the goal
`a(p)` in module user will be expanded to `a(user:p)`
thereby preserving the module information.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:- module(example,[a/1]).
:- meta_predicate a(:).
a(G) :- call(G)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An alternate mechanism is the directive [module_transparent/1](@ref module_transparent)
offered for compatibility with SWI-Prolog.
<ul>
<li>module_transparent + _Preds_ @anchor module_transparent
_Preds_ is a comma separated sequence of name/arity predicate
indicators (like
[dynamic/1](@ref dynamic)). Each goal associated with a transparent declared
predicate will inherit the context module from its parent goal.
</li>
</ul>
@subsection RehYExporting_Modules Re-Exporting Predicates From Other Modules
It is sometimes convenient to re-export predicates originally defined in
a different module. This is often useful if you are adding to the
functionality of a module, or if you are composing a large module with
several small modules. The following declarations can be used for that purpose:
<ul>
<li>reexport(+ _F_) @anchor reexport
Export all predicates defined in file _F_ as if they were defined in
the current module.
</li>
<li>reexport(+ _F_,+ _Decls_)
Export predicates defined in file _F_ according to _Decls_. The
declarations may be of the form:
<ul>
<li>A list of predicate declarations to be exported. Each declaration
may be a predicate indicator or of the form `` _PI_ `as`
_NewName_'', meaning that the predicate with indicator _PI_ is
to be exported under name _NewName_.
</li>
<li>`except`( _List_)
In this case, all predicates not in _List_ are exported. Moreover,
if ` _PI_ `as` _NewName_` is found, the predicate with
indicator _PI_ is to be exported under name _NewName_ as
before.
</li>
</ul>
</li>
</ul>
Re-exporting predicates must be used with some care. Please, take into
account the following observations:
<ul>
<li>
The `reexport` declarations must be the first declarations to
follow the `module` declaration.
</li>
<li>
It is possible to use both `reexport` and `use_module`, but
all predicates reexported are automatically available for use in the
current module.
</li>
<li>
In order to obtain efficient execution, YAP compiles dependencies
between re-exported predicates. In practice, this means that changing a
`reexport` declaration and then *just* recompiling the file
may result in incorrect execution.
</li>
</ul>
@page BuilthYins Built-In Predicates Library

View File

@ -65,6 +65,9 @@ regardless of the cycle-length.
left as a responsibility to the user.
@see "Co-Logic Programming: Extending Logic Programming with Coinduction"
by Luke Somin et al.
@{
*/
:- meta_predicate coinductive(:).
@ -155,7 +158,7 @@ writeG_val(G_var) :-
%-----------------------------------------------------
/**************************************
/**
Some examples from Coinductive Logic Programming and its Applications by Gopal Gupta et al, ICLP 97
@ -191,6 +194,7 @@ i(s(N)) :- i(N).
get_code(_),
fail.
@}
**************************************/

View File

@ -8,10 +8,7 @@
* *
*************************************************************************/
/**
@file absf.yap
@defgroup abs_file_name File Name Resolution
/** @defgroup absf0 File Name Resolution
Support for file name resolution through absolute_file_name/3 and
friends. These utility built-ins describe a list of directories that
@ -21,8 +18,7 @@
@{
*/
**/
:- system_module( absolute_file_name, [absolute_file_name/2,
absolute_file_name/3,
@ -32,6 +28,7 @@
remove_from_path/1], ['$full_filename'/3,
'$system_library_directories'/2]).
:- use_system_module( '$_boot', ['$system_catch'/4]).
:- use_system_module( '$_errors', ['$do_error'/2]).
@ -39,8 +36,8 @@
:- use_system_module( '$_lists', [member/2]).
/**
absolute_file_name(+File:atom, +Options:list, +Path:atom) is nondet
absolute_file_name(-File:atom, +Path:atom, +Options:list) is nondet
@pred absolute_file_name(+File:atom, +Options:list, +Path:atom) is nondet
@pred absolute_file_name(-File:atom, +Path:atom, +Options:list) is nondet
_Options_ is a list of options to guide the conversion:
@ -121,7 +118,7 @@ absolute_file_name(File,Opts,TrueFileName) :-
'$absolute_file_name'(File,Opts,TrueFileName,absolute_file_name(File,Opts,TrueFileName)).
/**
absolute_file_name(+Name:atom,+Path:atom) is nondet
@pred absolute_file_name(+Name:atom,+Path:atom) is nondet
Converts the given file specification into an absolute path, using default options. See absolute_file_name/3 for details on the options.
*/
@ -461,7 +458,7 @@ absolute_file_name(File0,File) :-
atom_concat([P0,A,Atoms],NFile).
/**
path(-Directories:list) is det,deprecated
@pred path(-Directories:list) is det,deprecated
YAP specific procedure that returns a list of user-defined directories
in the library search-path.We suggest using user:file_search_path/2 for
@ -475,7 +472,7 @@ path(Path) :- findall(X,'$in_path'(X),Path).
atom_codes(X,S) ).
/**
add_to_path(+Directory:atom) is det,deprecated
@pred add_to_path(+Directory:atom) is det,deprecated
YAP-specific predicate to include directory in library search path.
We suggest using user:file_search_path/2 for
@ -484,7 +481,7 @@ path(Path) :- findall(X,'$in_path'(X),Path).
add_to_path(New) :- add_to_path(New,last).
/**
add_to_path(+Directory:atom, +Position:atom) is det,deprecated
@pred add_to_path(+Directory:atom, +Position:atom) is det,deprecated
YAP-specific predicate to include directory in front or back of
library search path. We suggest using user:file_search_path/2 for
@ -500,7 +497,7 @@ add_to_path(New,Pos) :-
'$add_to_path'(New,last) :- !, recordz('$path',New,_).
'$add_to_path'(New,first) :- recorda('$path',New,_).
/** remove_from_path(+Directory:atom) is det,deprecated
/** @pred remove_from_path(+Directory:atom) is det,deprecated
*/
remove_from_path(New) :- '$check_path'(New,Path),
@ -513,7 +510,7 @@ remove_from_path(New) :- '$check_path'(New,Path),
'$check_path'([N|S],[N|SN]) :- integer(N), '$check_path'(S,SN).
/**
user:library_directory(?Directory:atom) is nondet, dynamic
@pred user:library_directory(?Directory:atom) is nondet, dynamic
Dynamic, multi-file predicate that succeeds when _Directory_ is a
current library directory name. Asserted in the user module.
@ -529,7 +526,7 @@ remove_from_path(New) :- '$check_path'(New,Path),
:- dynamic user:library_directory/1.
/**
user:commons_directory(?Directory:atom) is nondet, dynamic
@pred user:commons_directory(?Directory:atom) is nondet, dynamic
*/
@ -538,7 +535,7 @@ remove_from_path(New) :- '$check_path'(New,Path),
:- dynamic user:commons_directory/1.
/**
user:prolog_file_type(?Suffix:atom, ?Handler:atom) is nondet, dynamic
@pred user:prolog_file_type(?Suffix:atom, ?Handler:atom) is nondet, dynamic
This multifile/dynamic predicate relates a file extension _Suffix_
to a language or file type _Handler_. By
@ -578,7 +575,7 @@ user:prolog_file_type(A, executable) :-
current_prolog_flag(shared_object_extension, A).
/**
user:file_search_path(+Name:atom, -Directory:atom) is nondet
@pred user:file_search_path(+Name:atom, -Directory:atom) is nondet
Allows writing file names as compound terms. The _Name_ and
_DIRECTORY_ must be atoms. The predicate may generate multiple

View File

@ -357,47 +357,6 @@ exo_files(Fs) :-
db_files(Fs) :-
'$load_files'(Fs, [consult(db), if(not_loaded)], exo_files(Fs)).
%
% stub to prevent modules defined within the prolog module.
%
module(Mod, Decls) :-
'$current_module'(prolog, Mod), !,
'$export_preds'(Decls).
'$export_preds'([]).
'$export_preds'([N/A|Decls]) :-
functor(S, N, A),
'$sys_export'(S, prolog),
'$export_preds'(Decls).
% prevent modules within the kernel module...
use_module(M,F,Is) :-
'$use_module'(M,F,Is).
'$use_module'(M,F,Is) :-
var(Is), !,
'$use_module'(M,F,all).
'$use_module'(M,F,Is) :-
nonvar(F), !,
strip_module(F, M0, F0),
'$load_files'(M0:F0, [if(not_loaded),must_be_module(true),imports(Is)], use_module(M,F,Is)),
( var(M) -> true
;
absolute_file_name( F0, F1, [expand(true),file_type(prolog)] ),
recorded('$module','$module'(F1,M,_,_),_)
).
'$use_module'(M,F,Is) :-
nonvar(M), !,
strip_module(F, M0, F0),
(
recorded('$module','$module'(F1,M,_,_),_)
->
'$load_files'(M0:F1, [if(not_loaded),must_be_module(true),imports(Is)], use_module(M,F,Is))
),
(var(F0) -> F0 = F1 ; absolute_file_name( F1, F2, [expand(true),file_type(prolog)] ) -> F2 = F0 ).
'$use_module'(M,F,Is) :-
'$do_error'(instantiation_error,use_module(M,F,Is)).
'$csult'(Fs, M) :-
'$extract_minus'(Fs, MFs), !,
@ -666,6 +625,110 @@ initialization(G,OPT) :-
print_message(Verbosity, loaded(included, Y, Mod, T, H)),
nb_setval('$included_file',OY).
/**
@addtogroup yapmodules
@{
**/
%
% stub to prevent modules defined within the prolog module.
%
module(Mod, Decls) :-
'$current_module'(prolog, Mod), !,
'$export_preds'(Decls).
'$export_preds'([]).
'$export_preds'([N/A|Decls]) :-
functor(S, N, A),
'$sys_export'(S, prolog),
'$export_preds'(Decls).
% prevent modules within the kernel module...
/** @pred use_module(? _M_,? _F_,+ _L_) is directive
SICStus compatible way of using a module
If module _M_ is instantiated, import the procedures in _L_ to the
current module. Otherwise, operate as use_module/2, and load the files
specified by _F_, importing the predicates specified in the list _L_.
*/
use_module(M,F,Is) :- '$use_module'(M,F,Is).
'$use_module'(M,F,Is) :-
var(Is), !,
'$use_module'(M,F,all).
'$use_module'(M,F,Is) :-
nonvar(F), !,
strip_module(F, M0, F0),
'$load_files'(M0:F0, [if(not_loaded),must_be_module(true),imports(Is)], use_module(M,F,Is)),
( var(M) -> true
;
absolute_file_name( F0, F1, [expand(true),file_type(prolog)] ),
recorded('$module','$module'(F1,M,_,_),_)
).
'$use_module'(M,F,Is) :-
nonvar(M), !,
strip_module(F, M0, F0),
(
recorded('$module','$module'(F1,M,_,_),_)
->
'$load_files'(M0:F1, [if(not_loaded),must_be_module(true),imports(Is)], use_module(M,F,Is))
),
(var(F0) -> F0 = F1 ; absolute_file_name( F1, F2, [expand(true),file_type(prolog)] ) -> F2 = F0 ).
'$use_module'(M,F,Is) :-
'$do_error'(instantiation_error,use_module(M,F,Is)).
/**
@pred reexport(+F) is directive
@pred reexport(+F, +Decls ) is directive
allow a module to use and export predicates from another module
Export all predicates defined in list _F_ as if they were defined in
the current module.
Export predicates defined in file _F_ according to _Decls_. The
declarations should be of the form:
<ul>
<li>A list of predicate declarations to be exported. Each declaration
may be a predicate indicator or of the form `` _PI_ `as`
_NewName_'', meaning that the predicate with indicator _PI_ is
to be exported under name _NewName_.
</li>
<li>`except`( _List_)
In this case, all predicates not in _List_ are exported. Moreover,
if ` _PI_ `as` _NewName_` is found, the predicate with
indicator _PI_ is to be exported under name _NewName_ as
before.
</li>
Re-exporting predicates must be used with some care. Please, take into
account the following observations:
<ul>
<li>
The `reexport` declarations must be the first declarations to
follow the `module` declaration.
</li>
<li>
It is possible to use both `reexport` and `use_module`, but
all predicates reexported are automatically available for use in the
current module.
</li>
<li>
In order to obtain efficient execution, YAP compiles dependencies
between re-exported predicates. In practice, this means that changing a
`reexport` declaration and then *just* recompiling the file
may result in incorrect execution.
</li>
</ul>
**/
'$reexport'( TOpts, File, Imports, OldF ) :-
'$lf_opt'(reexport, TOpts, Reexport),
( Reexport == false -> true ;
@ -675,6 +738,10 @@ initialization(G,OPT) :-
'$extend_exports'(File, Imports, OldF )
).
/**
@}
**/
%
% reconsult at startup...
%

View File

@ -253,6 +253,8 @@ system_message(error(permission_error(alias,new,P), Where)) -->
system_message(error(permission_error(create,Name,P), Where)) -->
{ object_name(Name, ObjName) },
[ 'PERMISSION ERROR- ~w: cannot create ~a ~w' - [Where,ObjName,P] ].
system_message(error(permission_error(import,M1:I,redefined,SecondMod), Where)) -->
[ 'PERMISSION ERROR- loading ~w: modules ~w and ~w both define ~w' - [Where,M1,SecondMod,I] ].
system_message(error(permission_error(input,binary_stream,Stream), Where)) -->
[ 'PERMISSION ERROR- ~w: cannot read from binary stream ~w' - [Where,Stream] ].
system_message(error(permission_error(input,closed_stream,Stream), Where)) -->

View File

@ -15,6 +15,210 @@
* *
*************************************************************************/
/**
\defgroup yapmodules The YAP module system
\section syapmods The YAP Module System
The YAP module system is based on the Quintus/SISCtus module
system. In this design, modules are named collections of predicates,
and all predicates belong to a single module. Predicates are only
visible within a module, or _private_ to that module, but the module
will most often will also define a list of predicates that are
_exported_, that is, visible to other modules.
The main predicates in the module system are:
* module/2 associates a source file to a module. It has two arguments: the name of the new module, and a list of predicates exported by the module.
* use_module/1 and use_module/2 can be used to load a module. They take as first argument the source file for the module. Whereas use_module/1 loads all exported predicates, use_module/2 only takes the ones given by the second argument.
YAP pre-defines a number of modules. Most system predicates belong to
the module `prolog`. Predicates from the module `prolog` are
automatically visible to every module. The `system` module was
introduced for SWI-Prolog compatibility, and in YAP mostly acts as an
alias to `prolog`.
YAP is always associated to a module, the current <em>source
module</em> or <em>type-in module</em>. By default, all predicates
read-in and all calls to a goal will be made to predicates visible to
the current source module, Initially, the source module for YAP is the
module `user`. Thus Prolog programs that do not define modules will
operate within the `user` module. In this case, all predicates will be
visible to all source files.
YAP also includes a number of libraries and packages, most of them
defining their own modules. Note that there is no system mechanism to
avoid clashes between module names, so it is up to the programmer to
carefully choose the names for her own program modules.
The main mechanism to change the current type-in module is by using
the module/2 declaration.This declaration sets the source module when
it starts consulting a file, and resets it at the end. One can set
the type-in module permanently by using the built-in `module/1`.
\subsection Explicit Naming
The module system allows one to _explicitly_ specify the source mode for
a clause by prefixing a clause with its module, say:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
user:(a :- b).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
it is also possible to type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
user:a :- user:b.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
both formulations describe the same clause, independently of the
current type-in module.
In fact, it is sufficient to specify the source mode for the clause's
head:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
user:a :- b.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if the current type-in module is `m`, the clause could also be written as:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
user:a :- m:b.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The compiler rewrites the source clauses to ensure that explicit calls
are respected, and that implicit calls are made to the current source
module.
A goal should refer to a predicate visible within the current type-in
module. Thus, if a goal appears in a text file with a module
declaration, the goal refers to that module's context (but see the
initialization/1 directive for more details).
Again, one can override this rule by prefixing a goal with a module to
be consulted. The following query:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
?- nasa:launch(apollo,13).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
invokes the goal `launch(apollo,13)` as if the current source
module was `nasa`.
YAP and other Prolog systems allow the module prefix to see all
predicates visible in the module, including predicates private to the
module. This rule allows maximum flexibility, but it also breaks
encapsulation and should be used with care. The ciao language proposes
a different approach to this problem, see \cite .
Modules are not always associated with a source-file. They
may range over several files, by using the
`include`directive. Moreover, they may not be associated to any source
file. As an example,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
?- assert( nasa:launch(apollo,13) ).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
will create a module `nasa`, if does not already exist. In fact it is
sufficient to call a predicate from a module to implicitly create the
module. Hence after this call:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
?- nasa:launch(apollo,13).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
there will be a `nasa`module in the system, even if nasa:launch/2 is
not at all defined.
\subsection Using_Modules Using Modules
By default, all procedures to consult a file will load the modules
defined therein. The two following declarations allow one to import a
module explicitly. They differ on whether one imports all predicate
declared in the module or not.
\subsection MetahYPredicates_in_Modules Meta-Predicates and Modules
Consider the files opl.pl and rel.pl:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
% opl.pl
:- module(opl,[set_min/2]).
% obtain the smallest value for the last argument of goal G
set_min(G, Min) :-
last_argument( G, A ),
findall( A, call(G, A), Vs),
sort( Vs, [Min|_]).
% rel.pl
:- module(rel,[min_last/2]).
:- use_module(opl).
node(1, 2).
node(1, 4).
node(2, 3).
node(0, 4).
node(1, 4).
min_last(First, Min) :-
rel_min( node(First, V), Min).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Calling `min_last(node(_,_),Min)` should return `Min = 2` and
min_last(node(0,_),Min) should return `Min = 4`. To obtain this
behavior we need to call rel:node/1 from opl:set_min/2. However,
`opl:set_min/2` has no way to know that the goal `G` is from module
`rel`.
The meta_predicate/1 declaration addresses this problem by informing
the compiler that arguments of a predicate are goals, clauses, clauses
heads or other terms related to a module, and that these arguments
must be prefixed with their source module:
In the example we need to declare set_min/2 as a meta-predicate that
calls its first argument:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
:- meta_predicate set_min(0,-).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The 0 in the first argument refers to the use of call/1. The second argument has a mode (output mode) declaration, but this is not used in YAP.
The compiler uses this declaration to rewrite the calls to set_min/1 so that the module of the first argument is made explicit. In the example this corresponds to rewriting min_last/2.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
min_last(First, Min) :-
rel_min( rel:node(First, V), Min).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note the prefix `rel` before passing the call to node/2.
This process is not entirely transparent. Namely, last_argument/2 is
now forced to deal with the term `rel:node(First, V)`, instead of the
simpler `node(First, V)`. The very useful built-in strip_module/3
extracts the module prefix. We thus obtain:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
last_argument( G, A) :-
strip_module( G, _M, T),
functor(T, _, Arity),
arg(Arity, T, A).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An alternate mechanism is the directive module_transparent/1 that is
offered for compatibility with SWI-Prolog.
\{
**/
:- system_module( '$_modules', [abolish_module/1,
add_import_module/3,
current_module/1,
@ -66,15 +270,252 @@
% start using default definition of module.
%
use_module(F) :-
'$load_files'(F, [if(not_loaded),must_be_module(true)], use_module(F)).
/**
\pred use_module( +Files ) is directive
loads a module file
This predicate loads the file specified by _Files_, importing all
their public predicates into the current type-in module. It is
implemented as if by:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
use_module(F) :-
load_files(F, [if(not_loaded),must_be_module(true)]).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notice that _Files_ may be a single file, or a list with a number
files. The _Files_ are loaded in YAP only once, even if they have been
updated meanwhile. YAP should also verify whether the files actually
define modules. Please consult load_files/3 for other options when
loading a file.
Predicate name clashes between two different modules may arise, either
when trying to import predicates that are also defined in the current
type-in module, or by trying to import the same predicate from two
different modules.
In the first case, the local predicate is considered to have priority
and use_module/1 simply gives a warning. As an example, if the file
`a.pl` contains:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
:- module( a, [a/1] ).
:- use_module(b).
a(1).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
and the file `b.pl` contains:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
:- module( b, [a/1,b/1] ).
a(2).
b(1).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
YAP will execute as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
?- use_module(a).
% consulting /Users/vsc/Yap/a.pl...
% consulting /Users/vsc/Yap/b.pl...
% consulted /Users/vsc/Yap/b.pl in module b, 0 msec 0 bytes
%
% Warning:
% at line 5 in /Users/vsc/Yap/a.pl,
% Module a redefines imported predicate b:a/1.
% consulted /Users/vsc/Yap/a.pl in module a, 0 msec 0 bytes
true.
?- a(X).
X = 1.
?- b(X).
ERROR!!
EXISTENCE ERROR- procedure b/1 is undefined, called from context prolog:$user_call/2
Goal was user:b(_131290)
?- a:b(X).
X = 1.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The example shows that the query `a(X)`has a single answer, the one
defined in `a.pl`. Calls to `a(X)`succeed in the top-level, because
the module `a` was loaded into `user`. On the other hand, `b(X)`is not
exported by `a.pl`, and is not available to calls, although it can be
accessed as a predicate in the module 'a' by using the `:` operator.
Next, consider the three files `c.pl`, `d1.pl`, and `d2.pl`:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
% c.pl
:- module( c, [a/1] ).
:- use_module([d1, d2]).
a(X) :-
b(X).
a(X) :-
c(X).
a(X) :-
d(X).
% d1.pl
:- module( d1, [b/1,c/1] ).
b(2).
c(3).
% d2.pl
:- module( d2, [b/1,d/1] ).
b(1).
d(4).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The result is as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
./yap -l c
YAP 6.3.4 (x86_64-darwin13.3.0): Tue Jul 15 10:42:11 CDT 2014
ERROR!!
at line 3 in /Users/vsc/Yap/bins/threads/d2.pl,
PERMISSION ERROR- loading /Users/vsc/Yap/bins/threads/c.pl: modules d1 and d2 both define b/1
?- a(X).
X = 2 ? ;
ERROR!!
EXISTENCE ERROR- procedure c/1 is undefined, called from context prolog:$user_call/2
Goal was c:c(_131290)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The state of the module system after this error is actually undefined.
**/
use_module(F) :- '$load_files'(F,
[if(not_loaded),must_be_module(true)], use_module(F)).
/**
\pred use_module(+Files, +Imports)
loads a module file but only imports the named predicates
This predicate loads the file specified by _Files_, importing their
public predicates specified by _Imports_ into the current type-in
module. It is implemented as if by:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
use_module(Files, Imports) :-
load_files(Files, [if(not_loaded),must_be_module(true),imports(Imports)]).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The _Imports_ argument may be use to specify which predicates one
wants to load. It can also be used to give the predicates a different name. As an example,
the graphs library is implemented on top of the red-black trees library, and some predicates are just aliases:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~pl
:- use_module(library(rbtrees), [
rb_min/3 as min_assoc,
rb_max/3 as max_assoc,
...]).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unfortunately it is still not possible to change argument order.
**/
use_module(F,Is) :-
'$load_files'(F, [if(not_loaded),must_be_module(true),imports(Is)], use_module(F,Is)).
/**
\pred module(+M) is det
set the type-in module
Defines _M_ to be the current working or type-in module. All files
which are not bound to a module are assumed to belong to the working
module (also referred to as type-in module). To compile a non-module
file into a module which is not the working one, prefix the file name
with the module name, in the form ` _Module_: _File_`, when
loading the file.
**/
module(N) :-
var(N),
'$do_error'(instantiation_error,module(N)).
module(N) :-
atom(N), !,
% set it as current module.
'$current_module'(_,N).
module(N) :-
'$do_error'(type_error(atom,N),module(N)).
/**
\pred module(+ Module:atom, +ExportList:list) is directive
define a new module
This directive defines the file where it appears as a _module file_;
it must be the first declaration in the file. _Module_ must be an
atom specifying the module name; _ExportList_ must be a list
containing the module's public predicates specification, in the form
`[predicate_name/arity,...]`. The _ExportList_ can also include
operator declarations for operators that are exported by the module.
The public predicates of a module file can be made accessible by other
files through loading the source file, using directives
use_module/1 or use_module/2,
ensure_loaded/1 and the predicates
consult/1 or reconsult/1. The
non-public predicates of a module file are not supposed to be visible
to other modules; they can, however, be accessed by prefixing the module
name with the `:/2` operator.
**/
'$module_dec'(N, Ps) :-
source_location(F, _),
'$add_module_on_file'(N, F, Ps),
'$current_module'(_,N).
'$module'(_,N,P) :-
'$module_dec'(N,P).
/**
\pred module(+ _M_,+ _L_, + _Options_) is directive
define a new module with options
Similar to module/2, this directive defines the file where it
appears in as a module file; it must be the first declaration in the file.
_M_ must be an atom specifying the module name; _L_ must be a
list containing the module's public predicates specification, in the
form `[predicate_name/arity,...]`.
The last argument _Options_ must be a list of options, which can be:
+ <b>filename</b>
the filename for a module to import into the current module.
+ <b>library( +File )</b>
a library file to import into the current module.
+ <b>hide( +Opt)</b>
if _Opt_ is `false`, keep source code for current module, if `true`, disable.
+ <b>export(+PredicateIndicator )</b>
Add predicates to the public list of the context module. This implies
the predicate will be imported into another module if this moduleis imported with use_module/1 and use_module/2.
+ <b>export_list(? _Mod_,? _ListOfPredicateIndicator_)</b>
The list _ListOfPredicateIndicator_ contains all predicates
exported by module _Mod_
Note that predicates are normally exported using the directive
`module/2`. The `export/1` argumwnt is meant to allow export from
dynamically created modules. The directive argument may also be a list
of predicates.
**/
'$module'(O,N,P,Opts) :- !,
'$module'(O,N,P),
'$process_module_decls_options'(Opts,module(Opts,N,P)).
@ -117,84 +558,39 @@ use_module(F,Is) :-
'$prepare_restore_hidden'(Old,New) :-
recorda('$system_initialisation', source_mode(New,Old), _).
module(N) :-
var(N),
'$do_error'(instantiation_error,module(N)).
module(N) :-
atom(N), !,
% set it as current module.
'$current_module'(_,N).
module(N) :-
'$do_error'(type_error(atom,N),module(N)).
'$module_dec'(N, Ps) :-
source_location(F, _),
'$add_module_on_file'(N, F, Ps),
'$current_module'(_,N).
'$add_module_on_file'(Mod, F, Exports) :-
recorded('$module','$module'(F0,Mod,_,_),R), !,
'$add_preexisting_module_on_file'(F, F0, Mod, Exports, R).
'$add_module_on_file'(Module, F, Exports) :-
'$convert_for_export'(all, Exports, Module, Module, TranslationTab, AllExports0, load_files),
'$add_to_imports'(TranslationTab, Module, Module), % insert ops, at least for now
'$add_module_on_file'(DonorMod, DonorF, Exports) :-
recorded('$module','$module'(DonorF, DonorMod, _, _),R),
% the module has been found, are we reconsulting?
(
DonorF \= OtherF
->
'$do_error'(permission_error(module,redefined,DonorMod, OtherFile, DonorF),module(Mod,Exports))
;
recorded('$module','$module'(DonorF,DonorM, _, _), R),
erase( R ),
fail
).
'$add_module_on_file'(DonorM, DonorF, Exports) :-
'$current_module'( HostM ),
( recorded('$module','$module'( HostF, HostM, _, _),_) -> true ; HostF = user_input ),
% first build the initial export tablee
'$convert_for_export'(all, Exports, DonorM, HostM, TranslationTab, AllExports0, load_files),
sort( AllExports0, AllExports ),
( source_location(_, Line) -> true ; Line = 0 ),
recorda('$module','$module'(F,Module,AllExports, Line),_).
'$add_to_imports'(TranslationTab, DonorM, HostM), % insert ops, at least for now
% last, export everything to the host: if the loading crashed you didn't actually do
% no evil.
recorda('$module','$module'(DonorF,DonorM,AllExports, Line),_).
'$extend_exports'(F, Exps , NewF) :-
recorded('$module','$module'(NewF,NMod, NewExports, _),_R),
recorded('$module','$module'(F, Module,OriginalExports,Line),R),
'$convert_for_export'(Exps, NewExports, NMod, NMod, _TranslationTab, NewExports1, load_files),
'$add_exports'( NewExports1, OriginalExports, Exports ),
sort( Exports, AllExports ),
erase(R),
recorda('$module','$module'(F,Module,AllExports,Line),_),
fail.
'$extend_exports'(_F, _Module, _NewExports).
'$add_exports'( [], Exports, Exports ).
'$add_exports'( [PI|NewExports], OriginalExports, [PI|Exports] ) :-
% do not check for redefinitions, at least for now.
'$add_exports'( NewExports, OriginalExports, Exports ).
% redefining the same previously-defined file, no problem.
'$add_preexisting_module_on_file'(F, F, Mod, Exports, R) :- !,
erase(R),
( recorded('$import','$import'(Mod,_,_,_,_,_),R), erase(R), fail; true),
'$extend_exports'(HostF, Exports, DonorF ) :-
( recorded('$module','$module'( DonorF, DonorM, _, DonorExports),_) -> true ; DonorF = user_input ),
( recorded('$module','$module'( HostF, HostM, _, _),_) -> true ; HostF = user_input ),
recorded('$module','$module'(HostF,HostM,AllExports, _Line), R), erase(R),
'$convert_for_export'(Exports, DonorExports, DonorM, HostM, _TranslationTab, AllReExports, reexport(DonorF, Exports)),
lists:append( AllReExports, AllExports, Everything0 ),
sort( Everything0, Everything ),
( source_location(_, Line) -> true ; Line = 0 ),
recorda('$module','$module'(F,Mod,Exports, Line),_).
'$add_preexisting_module_on_file'(F,F0,Mod,Exports,R) :-
b_getval('$lf_status', TOpts),
'$lf_opt'(redefine_module, TOpts, RM),
( RM == false
->
'$do_error'(permission_error(module,redefined,Mod),module(Mod,Exports)), !
;
RM == true
->
'$add_preexisting_module_on_file'(F, F, Mod, Exports, R), !
).
'$add_preexisting_module_on_file'(F,F0,Mod,Exports,R) :-
repeat,
format(user_error, "The module ~a is being redefined.~n Old file: ~a~n New file: ~a~nDo you really want to redefine it? (y or n)",[Mod,F0,F]),
'$mod_scan'(C), !,
( C is "y" ->
'$add_preexisting_module_on_file'(F, F, Mod, Exports, R)
;
'$do_error'(permission_error(module,redefined,Mod),module(Mod,Exports))
).
'$mod_scan'(C) :-
stream_property(user_input,tty(true)),
stream_property(user_error,tty(true)),
!,
repeat,
get0(C),
'$skipeol'(C),
(C is "y" -> true ; C is "n" -> true ; C is "h" -> true ; C is "e" -> halt(1) ; format(user_error, ' Please answer with ''y'', ''n'', ''e'' or ''h'' ', []), fail), !.
'$mod_scan'(C) :- C is "n".
recorda('$module','$module'(HostF,HostM,Everything, Line),_).
'$module_produced by'(M, M0, N, K) :-
recorded('$import','$import'(M,M0,_,_,N,K),_), !.
@ -478,6 +874,15 @@ expand_goal(G, G).
% module_transparent declaration
%
/** \pred module_transparent( + _Preds_ ) is directive
_Preds_ can access the calling context.
_Preds_ is a comma separated sequence of name/arity predicate
indicators (like in dynamic/1). Each goal associated with a
transparent declared predicate will inherit the context module from
its parent goal.
*/
:- dynamic('$module_transparent'/4).
@ -504,6 +909,24 @@ expand_goal(G, G).
% directive now meta_predicate Ps :- $meta_predicate(Ps).
/** meta_predicate(_G1_,...., _Gn) is directive
Declares that this predicate manipulates references to predicates.
Each _Gi_ is a mode specification.
If the argument is `:`, it does not refer directly to a predicate
but must be module expanded. If the argument is an integer, the argument
is a goal or a closure and must be expanded. Otherwise, the argument is
not expanded. Note that the system already includes declarations for all
built-ins.
For example, the declaration for call/1 and setof/3 are:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:- meta_predicate call(0), setof(?,0,?).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
:- dynamic('$meta_predicate'/4).
:- multifile '$meta_predicate'/4.
@ -753,7 +1176,7 @@ export(Resource) :-
export_resource(Resource).
export_resource(Resource) :-
var(Resource),
var(Resource), !,
'$do_error'(instantiation_error,export(Resource)).
export_resource(P) :-
P = F/N, atom(F), number(N), N >= 0, !,
@ -943,25 +1366,27 @@ export_list(Module, List) :-
% dereference MI to M1, in order to find who
% is actually generating
( '$module_produced by'(M1, MI, N, K) -> true ; MI = M1 ),
( '$module_produced by'(M2, Mod, N, K) -> true ; M = M2 ),
( '$module_produced by'(M2, Mod, N, K) -> true ; Mod = M2 ),
M2 \= M1, !,
b_getval('$lf_status', TOpts),
'$lf_opt'(redefine_module, TOpts, Action),
'$redefine_action'(Action, M1, M2, M, N/K).
'$redefine_action'(Action, M1, M2, M, ContextM, N/K).
'$check_import'(_,_,_,_).
'$redefine_action'(ask, M1, M2, M, N/K) :-
'$redefine_action'(ask, M1, M2, M, _, N/K) :-
stream_property(user_input,tty(true)), !,
format(user_error,'NAME CLASH: ~w was already imported to module ~w;~n',[M1:N/K,M2]),
format(user_error,' Do you want to import it from ~w ? [y, n, e or h] ',M),
'$mod_scan'(C),
( C =:= 0'e -> halt(1) ;
C =:= 0'y ).
'$redefine_action'(true, M1, _, _, _) :- !,
'$redefine_action'(true, M1, _, _, _, _) :- !,
recorded('$module','$module'(F, M1, _MyExports,_Line),_),
unload_file(F).
'$redefine_action'(false, M1,M2, M, N/K) :-
'$do_error'(permission_error(import,M1:N/K,redefined,M2),module(M)).
'$redefine_action'(false, M1, M2, M, ContextM, N/K) :-
recorded('$module','$module'(F, ContextM, _MyExports,_Line),_),
'$current_module'(_, M2),
'$do_error'(permission_error(import,M1:N/K,redefined,M2),F).
% I assume the clause has been processed, so the
% var case is long gone! Yes :)
@ -1051,15 +1476,17 @@ delete_import_module(Mod, ImportModule) :-
'$set_source_module'(Source0, SourceF) :-
current_module(Source0, SourceF).
/** '$declare_module'(+Module, +Super, +File, +Line, +Redefine) is det.
/**
\pred declare_module(+Module, +Super, +File, +Line, +Redefine) is det
declare explicitely a module
Start a new (source-)module
@param Module is the name of the module to declare
@param File is the canonical name of the file from which the module
\param Module is the name of the module to declare
\param File is the canonical name of the file from which the module
is loaded
@param Line is the line-number of the :- module/2 directive.
@param Redefine If =true=, allow associating the module to a new file
\param Line is the line-number of the :- module/2 directive.
\param Redefine If `true`, allow associating the module to a new file
*/
'$declare_module'(Name, _Test, Context, _File, _Line) :-
add_import_module(Name, Context, start).
@ -1103,3 +1530,8 @@ ls_imports.
'$system_module'('system').
'$system_module'('$attributes').
/**
\}
**/