Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define parts of a predicate across several modules

I'm trying to write a predicate move/3 which handles several kinds of terms, each kind of which is defined in a separate file. I'm trying to use modules for this, because the files contain other predicates which should be namespaced appropriately.

So, I've created a module cat.prolog with contents:

:- module(cat, [move/3]).
:- multifile(move/3).

move(cat(C), P, cat(C2)) :-
   ...

Similarly for dog.prolog.

And main.prolog with:

:- use_module(['cat.prolog'], [move/3]).
:- use_module(['dog.prolog'], [move/3]).

(various predicates that use move/3 and expecting the clauses from all imported modules to be applicable.)

Trying to run this in SWI-Prolog:

?- ['main.prolog'].
%  cat.prolog compiled into cat 0.00 sec, 4,800 bytes
ERROR: Cannot import dog:move/3 into module user: already imported from cat
Warning: /home/edmund/main.prolog:2:
        Goal (directive) failed: user:use_module([dog.prolog],[move/3])
% main.prolog compiled 0.00 sec, 10,176 bytes
true.

At this point I can use dog:move/3 and cat:move/3 but not move/3. It works for the cat case but not the dog case.

I get the feeling there's a really obvious way to do this. I've tried combining modules and imports and multifile directives in many ways and still not found it yet...

like image 733
Edmund Avatar asked Dec 20 '22 18:12

Edmund


2 Answers

The multifile/1 syntax is simple, but the documentation lacks a simple example...

I created 3 modules files: pets.pl, cat.pl, dog.pl.

:- module(pets, [test/0, move/3]).
:- multifile move/3.
move(A,B,C) :- writeln(pets-move(A,B,C)).
test :- forall(move(A,B,C), writeln(move(A,B,C))).

:- module(cat, []).
:- use_module(pets).
pets:move(A,B,C) :- writeln(cat-move(A,B,C)).

:- module(dog, []).
:- use_module(pets).
pets:move(A,B,C) :- writeln(dog-move(A,B,C)).

Note the relevant syntax Module:Pred :- ... in the 'dependent' files

?- [cat,dog].
%  pets compiled into pets 0.00 sec, 3 clauses
% cat compiled into cat 0.01 sec, 7 clauses
% dog compiled into dog 0.00 sec, 3 clauses
true.

?- test.
Correct to: "pets:test"? yes
pets-move(_G41,_G42,_G43)
move(_G41,_G42,_G43)
cat-move(_G41,_G42,_G43)
move(_G41,_G42,_G43)
dog-move(_G41,_G42,_G43)
move(_G41,_G42,_G43)
true.

?- 
like image 180
CapelliC Avatar answered Jan 17 '23 16:01

CapelliC


A second working solution, this time without using the Logtalk extension to Prolog, following up my comment about using the include/1 directive:

----- common.pl -----
:- export(move/1).
---------------------

----- cat.pl -----
:- module(cat, []).
:- include(common).

move(cat).
---------------------

----- dog.pl -----
:- module(dog, []).
:- include(common).

move(dog).
---------------------

$ swipl
...
?- use_module(cat, []), use_module(dog, []).
% cat compiled into cat 0.00 sec, 4 clauses
% dog compiled into dog 0.00 sec, 4 clauses
true.

?- cat:move(X).
X = cat.

?- dog:move(X).
X = dog.

?- module_property(cat, exports(Exports)).
Exports = [move/1].

?- module_property(dog, exports(Exports)).
Exports = [move/1].

Like in the Logtalk solution, multifile predicates are not the answer.

It should be noted that, in most Prolog module systems, including SWI-Prolog, any module predicate can be called using explicit qualification as in the queries above. But using a file to hold the common bits have still two advantages: (1) it makes explicit in a single place the common bits and avoids source code duplication; (2) well behaved applications should only call exported predicates and there are tools that can detect violation of this principle. On the down side, an included file is only a first class entity of you consider files itself as such. Care must also be taking when loading the modules that include the common bits file as usage of e.g. consult/1 or use_module/1 will result in conflicts:

?- [cat, dog].
% cat compiled into cat 0.00 sec, 4 clauses
ERROR: import/1: No permission to import dog:move/1 into user (already imported from cat)
% dog compiled into dog 0.00 sec, 4 clauses
true.

In practice, this solution may result in most or all module predicates to be called using explicit qualification.

like image 25
Paulo Moura Avatar answered Jan 17 '23 15:01

Paulo Moura