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:
Autoload handling
Prototype implementation (on git swipl-devel, master)
-
Added
:- autoload(File)and:- 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 theautoloadflag. This flag has these values:-
false
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. -
explicit
Do not autoload from autoload libraries, but do
use lazy loading for predicates imported using autoload/1,2. This
is semantically the same asfalse, but preserves lazy loading. -
user
Asfalse, 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. -
user_or_explicit
Combinesexplicitwithuser,
providing lazy loading of predicates imported using
autoload/1,2 and implicit access to the whole library for
the toplevel. -
true
Provide full autoloading everywhere. This is
the default.
-
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 init.pl which
enables <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
After
?- 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
Modes
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.
The mode false is probably useful for creating stand-alone programs
reliably.
The mode 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 user space
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)).
The mode user, as 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.
Problems
The main remaining problem has to do with the role of the user module.
SWI-Prolog provides a module inheritance system where module X
inherits from 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 markediso. - Share operators. Operator lookup follows the module inheritance
relations. This allows defining operators inuserfor compatibility
with many systems that have a single global operator space. - Inherit the
unknownflag. - Create a pipeline for term_expansion/2, which is first applied in the
local module, then inuserand finally insystem.
The module inheritance system for predicates interacts with unknown and
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. -
Move
userto be cousins of the other modules rather than the
parent. The problem is that this breaks the desirable relations
for operators,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 fromuserand non-module files are by
default still loaded intouser, possibly not that much? For
the toplevel to load non-module files intouseritself
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.
Any opinions?
Plan
Regardless on how the above is resolved, there are plans for two
next steps:
- Resolve term/goal expansion. That could impact the above as it
may resolve one of the issues wrt. theusermodule. - Use autoload consistenty throughout all libraries and extensions
such that the system becomes fully functional regardless of the
autoload mode. - Complete and make the tool I now have to create the autoload
directives available, probably both as command line tool and
integrated into PceEmacs.
Running the Logtalk tests, there are some errors, however (I know that this is work in progress; no criticism here). One of them is with JPL, more exactly, the 