You need a main file for the module, e.g. main.pl
and
several subfiles, e.g. sub1.pl
, sub2.pl
, ... Lay
out the main file as follows:
:- module(ModuleName, ExportList). ... clauses/directives ... :- ensure_loaded(sub1). .. clauses/directives ... :- ensure_loaded(sub2).
The subfiles can contain any clauses and directives,
including ensure_loaded/1
directives, but not
module/2
directives.
An alternative is to use an include
declaration.
This can be done by defining user:message_hook/3
appropriately:
| ?- [user:user]. % consulting user... | message_hook(error,_,Lines) :- print_message_lines(user_error,error,Lines), abort. | ^D % consulted user in module user, 0 msec 424 bytes
This will intercept any error message, print the message, and abort:
| ?- [user]. % consulting user... | p p. ! Syntax error ! operator expected after expression ! in line 39 ! p ! <<here>> ! p . % consulted user in module user, 0 msec -16 bytes % Execution aborted
There is support for unfolding predicates at compile time:
user:goal_expansion/3
. For example, assume that
is_ornode(
X)
(is_andnode(
X)
) is true if the
shape (principal functor) of X is or/2
(and/2
). If you consult the following:
:- multifile user:goal_expansion/3. :- dynamic user:goal_expansion/3. user:goal_expansion(is_ornode(Term), _, Term=or(_,_)). user:goal_expansion(is_andnode(Term), _, Term=and(_,_)). plan_tree( [N|_Rest], _GuidanceNodes, _Indent ) :- is_andnode(N). plan_tree( [N|_Rest], _GuidanceNodes, _Indent ) :- is_ornode(N).
then plan_tree/3
becomes transformed to:
plan_tree([A|_], _, _) :- A=and(_,_). plan_tree([A|_], _, _) :- A=or(_,_).
If you are using fcompile/1
, make sure that the definition of
user:goal_expansion/3
, and anything else that the
compiler needs to know, has been loaded at fcompile time.
A common idiom is:
| ?- ensure_loaded(SetOfFiles), fcompile(SetOfFiles).
Note that fcompile/1
is obsolescent with the introduction of
partial saved-states (.po
files).
Note also that plan_tree/3
will not be able to
determinately select a matching clause based on A
,
as predicates are indexed on the shape of the first
argument only, which is a list in both clauses. Achieving
indexing on A
is the subject of the next question.
:-
to determinately select a
matching clause?
Consider the following clauses, with the above goal expansion:
plan_tree( [N|_Rest], _GuidanceNodes, _Indent ) :- is_andnode(N). plan_tree( [N|_Rest], _GuidanceNodes, _Indent ) :- is_ornode(N).
In SICStus, as in most WAMs, indexing is done on the shape of
the first argument. If all arguments are distinct
variables A, B, C, ..., and the first
goal is A
= Term
, indexing will be done on
the shape of Term
.
So to enable indexing on the shape of N
, you must
transform the clause e.g. to:
plan_tree( [N|_Rest], _GuidanceNodes, _Indent ) :- plan_tree_flat( N, _Rest, _GuidanceNodes, _Indent ). plan_tree_flat(N, _Rest, _GuidanceNodes, _Indent ) :- is_andnode(N). plan_tree_flat(N, _Rest, _GuidanceNodes, _Indent ) :- is_ornode(N).
With the above goal expansion, this code will indeed index
on N
.
Here's some code that purports to print all the Fibonacci numbers.
The author of the code expected retract/1
to backtrack
forever, finding ever new fibs/2
facts.
print_fibs :- retractall(fib(_)), assert(fibs(1,1)), retract(fibs(F1,F2)), write(F1), nl, F3 is F1+F2, assert(fibs(F2,F3)), fail.
If you run it:
| ?- print_fibs. 1 no
It doesn't work because of the semantics for calls to dynamic predicates in the presence of asserts and retracts. SICStus Prolog complies with the ISO Prolog standard in this respect. Clause 7.5.4 of the standard reads:
Any change in the database that occurs as the result of executing a goal (for example, when the activator of a subgoal is a call ofassertz/1
orretract/1
) shall affect only an activation whose execution begins afterwards. The change shall not affect any activation that is currently being executed.
In the above example, the retract/1
goal is unaffected by
the subsequent assert
, and only succeeds once.
X=[97|X], name(A,X).
loops. Is it a bug?
It is possible, and sometimes useful, to write programs that unify a variable to a term in which that variable occurs, thus creating a cyclic term. The usual LP theory forbids the creation of cyclic terms, dictating that an occurs-check should be done each time a variable is unified with a term. Unfortunately, an occurs-check would be so expensive as to render Prolog impractical as a programming language. Thus cyclic terms may be created and may cause loops trying to print them.
SICStus Prolog mitigates the problem by its ability to unify,
compare assert, and copy cyclic terms without looping. The
write_term/[2,3]
built-in predicate can optionally handle
cyclic terms. Unification with occurs-check is
available as a built-in predicate. Predicates testing
(a)cyclicity are available in a library package. Other
predicates usually do not handle cyclic terms well.