This relates to Autoload and qsave (swipl -c) don't go together. I think I made a lot of progress addressing this issue. Here is the status and remaining problems:
Prototype implementation (on git
:- autoload(File, Imports). Syntax
and semantics are a subset of
:- use_module, except no loading
happens (see below for exceptions). The autoload/2 variant defines
the specified predicates such that a local definition is prevented.
Both directives add some data to the module that defines autoloading
depending on the
autoloadflag. This flag has these values:
Predicates are never auto-loaded. If predicates
have been imported before using autoload/1,2, load the
referenced files immediately using use_module/1,2. Subsequent autoload/1,2 is mapped directly to use_module/1,2 Note that most of the development utilities such as listing/1
now have to be explictly imported before they can be used
at the toplevel.
Do not autoload from autoload libraries, but do
use lazy loading for predicates imported using autoload/1,2. This
is semantically the same as
false, but preserves lazy loading.
false, but to autoload library predicates
into the global user module. This makes the development
tools and library implicitly available to the toplevel,
but not to modules.
providing lazy loading of predicates imported using
autoload/1,2 and implicit access to the whole library for
Provide full autoloading everywhere. This is
Autoloading has a preference: (1) autoload/2, (2) autoload/1 and (3) autoload from the library.
Status and results
Added autoload/2 declarations for all files in
library that come from
the core system. This implies that all dependencies for these core
libraries are now explicit. Using my default personal
<N> ?- history and XSB compatible loading of
.P files, startup
time resource usage is reduced significantly:
Before (Intel® Core™ i5-6260U, M2 SSD, Fedora 30)
?- statistics. % Started at Tue Feb 11 09:56:26 2020 % 0.206 seconds cpu time for 726,531 inferences % 6,515 atoms, 4,580 functors, 3,615 predicates, 74 modules, 136,107 VM-codes
?- statistics. % Started at Tue Feb 11 10:01:17 2020 % 0.086 seconds cpu time for 247,031 inferences % 6,001 atoms, 4,154 functors, 2,925 predicates, 49 modules, 102,780 VM-codes
The default mode is
true which, if all libraries have been updated to
use autoload/2 where possible, will remain fully compatible with the
current system while providing two benefits: (1) reduced load time and
memory usage and (2) a guarantee that all relevant code is loaded if
autoload is switched to
false is probably useful for creating stand-alone programs
user_or_explicit is intended for developing programs that
eventually want to go for
false for creating an executable. While
developing it does the loading lazily which reduces startup time and
in addition it allows you to run whatever you like in
without declarations. This includes tools (listing, gxref, gtrace,
edit, profile, statistics, …) and running stuff like this to test
stuff inside your modules without importing time/1, forall/2, etc.
?- time(forall(between(1, 1 000 000, _), mym:mygo)).
false, materializes all autoload/1,2 and, as
user_or_explicit ensures the toplevel remains friendly.
Portability and compatibility
Several people here have argued for controlled import using use_module/2
(or now autoload/2). The advantage is that you can easily see what comes
from where without using tools. Another advantage is that it is
completely obvious what needs to be loaded for creating an executable
and thus there is no need to rely on the code analysis to include all
dependencies. As we know, code analysis in Prolog is by definition
potentially incomplete. I have seen actual production code using
atom_concat(X,Y,Name), call(Name, ...)
There is also a price to pay. In the past predicates have often been
moved from built-in to the library or the other way around. Migrating to
the library reduces the size of the core while migration to a built-in
can use a low-level C implementation to gain performance. Migration
between libraries also happened, either for compatibility with other
Prolog systems or just to clean up the organization. Using autoload is
true, this doesn’t affect any code. If use_module/2 or autoload/2 is
used and autoloading is disabled, this does break applications.
Note that the fear that running code breaks as a result of using the
autoloader is not justified. Programs first use the predicates they
define themselves, unless these conflict with the ISO core. Anything
else comes from built-in or the autoloader, where the system development
should guarantee a name/arity available as built-in or autoloading is
(1) not ambiguous, (2) does not disappear and (3) does not change
semantics. The latter (changing semantics) happens occasionally for
compatibility or due to new insights.
A similar argument applies to porting e.g., SICStus code. Typically we
can simply remove most of the use_module/1,2 calls and let the auto
loader do its magic. With explicit loading you will probably end up with
a lot of
:- if(...) or significant extension of the dialect
emulation libraries. For this reason SICStus and SWI long time ago
agreed on a
:- require([PredicateIndicators])., meaning “Get these
things from somewhere” that was dynamically mapped to use_module/2 in
SICStus and ignored in SWI-Prolog as the autoloader would take care.
SWI-Prolog’s require will be changed to depend on the autoload flag.
The main remaining problem has to do with the role of the
SWI-Prolog provides a module inheritance system where module X
user which inherits from
system where all the
built-ins live (by default, the hierarchy can be changed). The
inheritance mechanism does:
- Make predicates available. These can be overruled locally, except
for those marked
- Share operators. Operator lookup follows the module inheritance
relations. This allows defining operators in
with many systems that have a single global operator space.
- Inherit the
- Create a pipeline for term_expansion/2, which is first applied in the
local module, then in
userand finally in
The module inheritance system for predicates interacts with
autoloading. This leads to weird behaviour if these are changed from their
defaults, such as
:- module(m, [m/1]). m(X) :- member(X, `aap`).
Now, without autoloading, calling m/1 raises an exception. Now call
or import member/2 in
user and suddenly m/1 runs fine. This is of
course unacceptable. I see some ways out:
Block inheritance of predicates from
userusing some flag. This
probably has the least implications. It feels bad to change the
general inheritance relation for a specific module though.
userto be cousins of the other modules rather than the
parent. The problem is that this breaks the desirable relations
unknownand term expansion
Use a new module for the default interactive toplevel. It is a
bit unclear to me how much would be affected by this. If this
new module inherits from
userand non-module files are by
default still loaded into
user, possibly not that much? For
the toplevel to load non-module files into
is quite a kluge though. Loading code into the new toplevel
has some advantages, such that it starts out empty rather than
containing the various hooks that the system inherrited from
other Prolog implementations and its past.
Regardless on how the above is resolved, there are plans for two
- Resolve term/goal expansion. That could impact the above as it
may resolve one of the issues wrt. the
- Use autoload consistenty throughout all libraries and extensions
such that the system becomes fully functional regardless of the
- Complete and make the tool I now have to create the autoload
directives available, probably both as command line tool and
integrated into PceEmacs.