lib.pl -- Predicate based code development.

This pack implements methods for loading code into SWI Prolog programs. One of the major innovations the library introduces, is that of progressive, lazy loading of packs. That is, if only a specific predicate is (lazily) required from a pack(lib)-aware pack, only that and its dependent code will be loaded.

That is, your code can load things like

?- lib( stoics_lib:kv_compose/3 ).
?- lib( stoics_lib:kv_decompose/3 ).

and only the relevant parts of the pack(stoics_lib) will be loaded.

If later on your code decides to do a

?- lib(stoics_lib).

The remainder of the library loads up quietly and politely.

Please note that this is, at top level at least, orthogonal to any other loading.

You can still do

?- use_module( library(stoics_lib) ).

and get the whole thing into memory.

Pack(lib) plays reasonably well with the documentation server. Bar, the normal limitations of the server. By convention and to help locating the module docs, lazy packs should define (Pack)/0 predicate in same file as the mods docs. Searching for that on doc server, should make it easy enough to get to it.

Although this library, pack(lib), contains a number of involved features it can also be used as a straight forward shorthand, replacement for use_module(library(Lib)).

?- lib(Atomic).

is equivelant to use_module(library(Atomic)) if Atomic is a system library or an installed Pack, while it will interogate the SWI pack server for matching packs if Atomic is atomic and not an installed pack.

In addition the library allows for loading with initializations turned off.

A good example of how to create a lazy pack is pack(stoics_lib), http://stoics.org.uk/~nicos/sware/stoics_lib v0.3. An example of how to lazy load things from stoics_lib is the latest pack(debug_call) http://stoics.org.uk/~nicos/sware/debug_call v0.4.

Repositories

Code is managed in repositories (also repo) that can be either packs or libs.

A pack is a unit of programs as managed by built-in SWI package manager (library(prolog_pack)). A lib (library) is a directory containing a number of program files.

pack(lib) supports a number of ways to organise your code and load it, but it comes to its own when code s organised as predicate-in-a-file fashion. In this mode of development a predicate such as kv_decompose/3 would be defined on file kv_decompose.pl which will only containing code for defining this predicate, with the possible exception of helper predicates that are too specific to be of outside interest.

kv_decompose( [], [], [] ).
kv_decompose( [K-V|T], [K|Tk], [V|Tv] ) :-
        kv_decompose( T, Tk, Tv ).

Lib code is considered as coming from the special pack user.

Code-tables

Associated with each repository are 2 types of code-tables: (code-)_indices_ and (file-)_locators_.

A code-index maps a predicate identifier along with its source repo to an absolute file name of the source that defines it. Indices are of the form:

lib_tables:lib_index( Pname, Parity, Repo, AbsFile ).

File locators store all filenames in a repository. These can be named matched predicate names that need to be loaded. pack(lib) can be directed to assume that files from a specific repository exhibit this homonyms property. Locators are of the form:

lib_tables:lib_homonym( Stem, Repo, AbsFile ).

For each index file loaded for a repository, the following is asserted:

lib_tables:lib_loaded_indices(Repo,File)

and for each locator

lib_tables:lib_loaded_homonyms(Repo,Stem,File)

When loading a repository the user can choose whether to load indices and locators independently.

Loading source code

During the process of loading code into memory, lib/1 and /2 directives are used to locate code to which the specific code depends.

There are three main categories of operations:

These operations are all specific to the loading context. This is achieved by creating meta-predicates that identify which part of the repository base each context has access to.

Attachment of repository is registered via lib_tables:lib_attached_indices(To,PackIG) and lib_tables:lib_attached_homonyms(To,PackFG).

lib_tables:lib_attached_indices(bims,options).
lib_tables:lib_attached_homonyms(bims,false).

attaches the indices but not the file locators.

Since all code from directory-libs load to a single module (user), loading code has either access to all such code, or to none.

Conventions

Packs are expected to have matching top directories and main files. The main file of a pack should be within top directory prolog/. (The directory convention is set by library(prolog_pack)). For example for pack bims the following file should exist in packs directory:

bims/prolog/bims.pl

For packs the main code directory is src/. Additionally src/lib and src/auxil are treated as code directories.

Internals

Variables

Repo
repository
Pack
prolog pack (installed or locally addressed)
Lib
directory containing code
Root
absolute reference to the root directory of a Repo
Pn
predicate name
Idx
library index term
Hmn
library file homonym term
Pa
predicate arity
Cxt
context module

Predicate names

Pack info

This is a complete re-write of pack(requires) v1.1.

Listens to debug(lib).

author
- nicos angelopoulos
version
- 1.0 2017/3/6
- 1.1 2017/3/9, lazy loading
- 1.2 2017/3/11, fixed missing cut, added lib(version(V,D))
- 1.3+4 2017/8/8, fixed multi-source for user, improved contact to server, install while lazy loading
- 1.5 2017/8/15
- 1.6 2018/3/18, lib/2 suggests(), lib/2, promise() via hot-swapping, private packs
- 1.7 2018/4/5, auto-install missing was broken
See also
- http://stoics.org.uk/~nicos/sware/lib
 lib(+Operand)
 lib(+Operand, +Opts)
Loads code or/and indices of Repo into the current context.

When Repo homonym(Repository) then only the homonims of local dir (adjusted for pack dir structure) are added to as coming from Repository.

Operands

SysLibrary
An installed library (atomic). Is loaded with use_module( library(SysLibrary) ).
Pack
A pack known to SWI. If pack is not installed then the server is contacted to look for name-matching packs that can be installed. If there is at least one matching pack, it can be installed interactively.
LibDir
Declares a library directory should be
  • atomic, an absolute path
  • rel(Rel) Rel will be absolute_file_name/3 to be made into an absolute location
  • alias(Dir) as above, but ensures that nothing in Dir is interpreted as a command
  • compound compound terms are tried to be expanded to an existing directory

The default options for libs are

  • load(Load=false)
  • index(Idx=true)
  • homonyms(Hmns=true)
  • type(Type=lib)
Command
One of
homonyms(From)
attach homonyms From pack
init(Lib)
init(Lib, Cxt)
declare initilization call (library can be loaded without this firing, if so needed, as is the case for lib_mkindex/1)
suggests(Lib)
it is likely you need Lib for full functionalilty
promise(Pred, Load)
Pred is needed for functionality and it can be found by loading Load, but it will only happen at Pred's first call
expects(Pid, Mess)
expects(Pid, Mess, Call)
complains if Pid is not defined at loading time. Mess should be a debug style message with one ~w which will be called with Pid as its printing argument. If call is present, is called after the printing of the message.
version(Vers, Date)
return version and publication date

Opts

index(Idx)
whether to load indices
homonym(Hnym)
whether to load homonym file-locators
load(Load)
whether to load the main entry point of Repo
mode(Mode=self)
makes miising message more acurate (other value: suggests)
suggest(Dn=true)
suggest the library is downloaded if it is not locally installed ?
type(Type)
enforce a particular type of repository (pack or lib)

The defaults depend on whether Repo is a pack or a lib.

To be done
- when predicate is missing from stoics_lib while loading from b_real, we get clash between main and lazy, error should be clearer (the pred select_all/3 was actually not defined in file either)