requires.pl -- Predicate based code development

Loads libraries and code in the following modes

  1. libs in pack aware fashion
  2. code within the files of own pack
  3. code from other packs without loading the whole pack and
  4. from user specific library directories.

Installation and loading.

?- pack_install( requires ).
?- use_module( library(requires) ).

The following is a synonymous way of loading the library

?- use_module( library(lib) ).

Loading code

The library provides two directive predicates for loading code lib/1 and requires/1.

Originally, requires/1 was used for locating and loading predicates with

?- requires( kv_decompose/3 )

and lib/1 was developed to be a packs aware synonym of use_module(library(Pack)), such as

?- lib(lists).
?- lib(bims).

where lists is the system library traditionally loaded with use_module(library(lists)) and bims is an installed pack.

If the argument to lib/1 is not an installed library, then the predicate will look on the SWI server for installable packs.

?- lib(switex).
% Contacting server at http://www.swi-prolog.org/pack/query ... ok
p switex@0.0.7              - Literate programming with LaTeX and Prolog
Lib not found, but pack exists, do you want me to run ?- pack_install(switex). Y/n?

As the arguments described so far for requires/1 and lib/1 can be distinguished, lib/1 can also act as a synonym to requires:

?- lib( kv_decompose/3 ).

is identical to

?- requires( kv_decompose/3 ).

The main idea of pack(requires) is that the top of a source file contains a number of directives declaring dependencies to other source files. The pack works particularly well with predicate-in-single-file development where, for instance, kv_cecompose/3 is defined in src/lib/kv_decompose.pl. pack(requires) also works with index files (LibIndex.pl) that can point where specific predicates are located.

By default pack(requires) look into the following sub-directories for source code:

Loading code from own pack

By default requires/1 loads code by either

(a) looking into src/LibIndex.pl for a pointer to the file defining a specific predicate, or (b) searching for a file whose name matches the name of the to-be-loaded predicate.

Under scenario (a) above, a directive such as

:- requires( kv_decompose/3 ).

will match entry of LibIndex.pl

lib_index(kv_decompose, 3, any, user, 'lib/kv/kv_decompose').

and load file src/lib/kv/kv_decompose.pl.

Under scenario (b)

:- requires( kv_decompose/3 ).

will match the predicate name to the filename kv_decompose.pl and load the file.

Loading selective code from other packs

pack(requires) can be used to load specific predicates from packs that use the requires methodology. This can be done without loading the whole of the external pack. A good example of a pack allowing partial loading is stoics_lib. From there one can load a single predicate and its dependencies without loading the whole of the library with something like

?- requires( stoics_lib:kv_decompose/3 ).

and

?- lib( stoics_lib:kv_decompose/3 ).

The indices of all installed packages that contain src/LibIndex.pl can be made available to the system when library(requires) is loaded if the flag lib_indices is set to true:

?- set_prolog_file( lib_indices, true ).

or by calling

?- lib_load_pack_index.

which loads all available indices, or with

?- lib_load_pack_index( +Pack ).

which loads index for specific Pack.

Indices are loaded on predicate lib_index: pack_lib_index/4. Once the index of a pack has been consulted, its predicates can be loaded without prefixing.

?- lib_load_pack_index( stoics_lib ).
?- requires( kv_decompose/3 ).

Loading from local libraries

The pack also allows index based access to directories containg code. Particularly code that is fragmented and follows a single-entry-predicate-per-file. The main use scenario is that of declaring a number of directories as code repositories in a users Prolog start up file. Any Prolog session will then have easy access to loading the predicates within those directories.

As an example, assume source Prolog files are kept in ~/pl/lib. An index of all predicates can be created by

?- expand_file_name( '~/pl/src', PlSrc ), lib_mkindex( PlSrc ).

Assuming there is file term/en_list.pl defining en_list/2, then the above will create entry

lib_index(en_list, 2, any, user, 'lib/term/en_list').

Once the user enters the following directive to their initialisation file

?- expand_file_name('~/pl/src',PlLib), assert(library_directory(PlLib)).

then loading pack(requires) will assert

lib_index(en_list, 2, any, user, 'lib/term/en_list').

and the user will be able to load the predicate with

?- requires( en_list/2 ).

Creating library and pack indices.

Indices that are used to locate code within subdirecdtories can be created for directories and packs with

?- lib_mkindex( DirOrPack).

For packs, the main idea is that during development the system might rely on the filenames to locate loadable code where as before publication an index can be created.

See also lib_mkindex/0.

The library defines flag lib_homonym (Boolean) which controls whether the library seeks to load code by matching predicate names to filenames (see also lib_pack_start/2).

Pack info

This library is now in beta. Internally things might change, but interface should be stable.

author
- nicos angelopoulos
version
- 1.0.0 2017/2/21
To be done
- cleanup predicate for removing lib_index pack modules
- provide option for loading all preds from a pack such as stoics_lib
lib_pack_start(+Pack)
lib_pack_start(+Pack, +Homonym)
lib_pack_end(+Pack)
Used at the beginning of load sections (lib_pac_start/1) of a main pack file to signify that all local requires() calls should be interpretted as loads from Pack and at end of load sections (lib_pack_end/1) to release the lock.
This should not be necessary, but SWI 7.3.31-9-g57cb567 looses the prolog_load_context(module,_) when requires/1 is called from different file (even though we are loading within the correct module).
Homonym is a Boolean value stating whether requires will try to load predicate definitions from a file having the same name whithin the file structure of the pack or library directory.
author
- nicos angelopoulos
version
- 0.1 2016/12/10
- 1.0 2017/2/21
See also
- pack(options)
To be done
- report SWI behaviour and check to see if future versions behave differently
requires(+PPindicator, +Into)
Module Into requires predicate PPindicator
lib_version(-Version, -Date)
Version number in Mj:Mn:Fx format, and current publication date of current version.
requires(+PPindicator)
Loads source defining Pindicator. PPindcator can be either of the form Pack:Pname/Arity or of the form Pname/Arity.

Originally the predicate facilitated loading predicates from a lib directory with the explicit purpose of allowing publication of source code that included all necessary lib code. With the advent of packs the paradigm has shifted but the main idea remains that of predicate-in-a-file software development.

The predicate should be used as a declaration at the top of a file declaring dependencies to other predicates. For instance in the following example current_call/2 depends on goal_spec/2.

:- requires( stoics_lib:goal_spec/2 ).

current_call( Goal ) :-
    current_call( Goal, fail ).

current_call( Goal, _Else ) :-
    goal_spec( Goal, Spec ),
    current_predicate( Spec ),
    !,
    call( Goal ).
current_call( _Goal, Else ) :-
    call( Else ).

When PPindicator is of the form Pname/Arity where Pname is atomic and Arity is a positive integer requires/1 looks in the following places:

Pack:Pname/Arity if Pack is the current context from which requires/1 was asked to load a parent of the predicate (see below) or if Pack is a current moduled pack into which code is to be loaded

lib_index:pack_lib_index(Pname,Arity,Pack,File)

lib_index:lib_index(Pname,Arity,_,_,File)

lib_index/5, is loaded from <library>/LibIndex.pl files. This corresponds to the original design where the only difference between public and private versions were the underlying location of the library directory.

When PPindicator is of the form Pack:Pname/Arity is loaded from Pack's sources. The following order is used to locate the appropriate file

If a predicate is already defined, there is no effort to verify that is loaded from any particular source. When debugging is turned on and info message will indicate the absolute location for the original source.

author
- nicos angelopoulos
version
- 0.3 2016/12/10, backward incompatibilities introduced
- 0.4 2017/2/20, major changes
lib_load_pack_index
Call Lib_load_pack_index/1 on all installed packs.
lib_load_pack_index(+Pack)
If Pack is installed and contains file src/LibIndex.pl then its clauses are loaded into module lib_index. Else, the predicate succeeds without any actions. Loading of the clauses make them available for loading without prefixing.
?- requires( stoics_lib:kv_decompose/3 ).
?- kv_decompose([a-1,b-2,c-3], Ls, Ns ).
Ls = [a, b, c],
Ns = [1, 2, 3].

compared to

?- requires( kv_compose/3 ).
ERROR: Cannot locate predicate: kv_compose/3, in pack context: none and into: user
false.

?- lib_load_pack_index( kv_compose/3 ).

?- lib_load_pack_index( stoics_lib ).

?- requires( kv_decompose/3 ).
lib_mkindex
Build indices for all packs.
lib_mkindex(+PackOrDir)
lib_mkindex(+PackOrDir, Opts)
Build index file for Pack or Dir

Opts (can be unlisted)

homonym(Homonym=false)
when true only add to index preds whose name matches the name of the file there in
load_mod(LMod=true)
if prolog/Pack.pl exists, shall we consult it before creating the index ?
mod(Mod)
module to use for loading
sub(Sub)
subdirectory withing PackOrDir. By default if argument is a pack, Sub is src else Sub == ''

Currently the predicate assumes that

(a) Pack/prolog/Pack.pl is the only prerequisite to loading Pack files to memory, and (b) that there is a src/ subdirectory.

The predicate uses loading of code and predicate_property calls to do all the heavy lifting. It thus creates file Pack/src/LibIndex.pl.

== ?- lib_mkindex( local_lib_dir, true ). ?_ lib_mkindex( options, true ). % make index for pack(options) deposited stc/LibIndex.pl ?- lib_mkindex( ++

author
- nicos angelopoulos
version
- 0.1 2017/2/18
lib(+Lib)
lib(+Libs)
A shorthand for use_module(library(Lib)). Libs is a list of library names.

In addition if current_prolog_flag(debug_initialization,true) succeeds, debug(Lib) is internally turned on for the load operation.

Finally if Lib is not installed pack, the predicate suggests loading it.

?- lib( bio_db ).
?- halt.
% swipl -f
?- use_module( library(requires) ).
?- set_prolog_flag( debug_initialization, true ).
?- lib( bio_db ).
lib(+Lib, +Module)
Load lib into Module.
lib_suggests(+LibS)
For a library or list of libraries load the libraries if they are available, else be quiet.
lib_expects(+PindS)
lib_expects/1 is a directive.

For each Pind in PindS (list or single predicate specification), the predicate does nothing if a matching predicate is defined in memory, else it prints a warning.

The raison d'ĂȘtre is to flag upstream loading one from possibly many alternatives implementation of a predicate that the current predicate depends on.

For example, see k_sub_graph/5. There, the native predicate depends on sub_graph/3. Irrespective of how graphs are represented k_sub_graph/5 only needs access to sub_graph/3 to do its job. Therefore the following directive is added to the source of k_sub_graph/5:

 :- lib_expects( sub_graph/3 ).
 ?- lib_expects( nothing/0 ).
 % Warning: Expected predicate nothing/0, not in memory
 ?- use_module( library(lists) ).
 ?- lib_expects( append/3 ).
 true.
author
- nicos angelopoulos
version
- 0.1 2014/7/25
lib_expects(+Expects, +Mess)
lib_expects(+Expects, +Mess, +Call)
lib_expects/2,3 is a directive.

Currently Expects should be of the form lib(Lib).

expects/2 will attempt to load Lib via use_module(library(Lib)). If that fails a warning containing Mess will be printed and Call will be called. Call usually defines operators that allow the rest of the code to compile without errors.

The idea is to flag missing components and describe in the message which part of a pack will be affected, but do not stop the loading as the core functionality of a pack is not affected.

To get the warning messages about missing expected libs, set the flag

?- set_prolog_flag( lib_expects_report, true ).
author
- nicos angelopoulos
version
- 0.1 2014/1/8