Error with discontinuous predicate

I’m writing a predicate to encode some typing information, but I want this to be multifile and discontiguous. So I have, in typeDecls.pl:

:- module(typeDecls, [(::)/2]).

:- op(500, xfx, ::).
:- multifile((::)).
:- discontiguous((::)).

[] :: list(_).
[X|Xs] :: list(A) :- X :: A, Xs :: list(A).

%%% Some more stuff

Then I might have other files that define data structures, with the associated typing information; for example, in file tree.pl, I have:

:- module(tree, [showTree/1]).
:- use_module(library(clpfd)).
:- use_module(typeDecls).

%%% NOTE this clause of the typing predicate
node(Node, Subforest) :: tree(NodeType) :-
    Node :: NodeType,
    Subforest :: list(tree(NodeType)).

showTree(T) :- showTree(T, 1).
showTree(node(Node, SubForest), Depth) :-
    once((
        writeLevel(Depth), write(' '), print(Node), write('\n'),
        DepthNext #= Depth+1,
        maplist({DepthNext}/[T]>>showTree(T, DepthNext), SubForest)
    )).

%%% Some more stuff

And wrapping this up, I might have in my main file something like:

:- use_module(typeDecls).
:- use_module(tree).

%%% Some more stuff

When I consult the main file, I’m getting the error:

ERROR: /home/.../tree.pl:5:22: Syntax error: Operator expected

and that position corresponds to my instance of the typing clause in tree.pl, as if that file couldn’t see the operator declaration in typeDecls.pl. Could you suggest how to fix this problem?

Thanks
Carlo

I just found the answer: https://www.swi-prolog.org/pldoc/man?section=moduleop

basically operator declarations are local to modules, and if you want them to be visible from the import list, you should use something like:

:- module(typeDecls, [op(500, xfx, ::), (::)/2]).

I’ll keep this thread open to ask questions related to this example, should they arise

Here’s a subsequent question: now that I fixed the import as in the previous response, I get these warnings:

Warning: /home/.../tree.pl:5:
Warning:    Local definition of tree:(::)/2 overrides weak import from typeDecls

I don’t want that override to happen! That’s why I declared (::)/2 as multifile in typeDecls.pl!
On the other hand, I tried to write something like:

:- module(typeDecls, [op(500, xfx, ::), multifile((::)), (::)/2]).

and that doesn’t work. So what’s the proper solution to signal that (::)/2 is multifile and thus should not be overrided?

I guess the core of this question is already contained in the comment at https://www.swi-prolog.org/pldoc/man?predicate=multifile/1 from @CapelliC

Thanks @j4n_bur53, I’ll try this tomorrow when I’ll be back at the code. But in the meantime: why are we qualifying only the :: functor in head? Should I also qualify the occurrences in the body, like

    typeDecl:(Node :: NodeType)

This should be

:- multifile((::)/2).
:- discontiguous((::)/2).

As is, it defines (::)/0. Actually it should raise an error. Will change that.

Both are valid, but mean something different. Qualifying the entire clause makes the clause behave the same as if it was defined in the indicated module. Qualifying the head adds a clause to the indicated module, but the body is executed in the context of the module where the clause appears. This is the most common way to deal with multifile predicates in modules.

3 Likes

Thanks for your answer, indeed the tree.pl file compiles without errors now.
I have problems with my main file: I had this clause before:

signature_decomposition({ A }, node(def, [])) :- A.

signature_decomposition(Term :: Type, node(Term :: Type, DecompositionList)) :-
    nonvar(Term),
    clause(Term :: Type, Constraints),
    conjunction_list(Constraints, ConstraintList),
    maplist(signature_decomposition, ConstraintList, DecompositionList).

signature_decomposition(Term :: Type, node(Term :: Type, [])) :-
    once(\+ clause(Term :: Type, _) ; var(Term)).

and was able to ask queries like:

%?- signature_decomposition(member(A, [1,2,3]) :: T, D).

whereas now, that query just return false, because I have predicates of the form user:(Term :: Type) instead of just Term :: Type. I can change the head of the clause but at this point it’s quite arbitrary and I don’t understand which rule I should follow. I tried adding:

:- meta_predicate signature_decomposition(:, -).

after reading the guide on metapredicates, but that didn’t help. I’d appreciate some guidance of how to get my code back to working.

Related question: can I write a term expansion so that every time A ::: B is read, it’s expanded in typeDecls:(A :: B)? Is this a common strategy for this problem?

This is (probably) the right direction. Note that the predicate is then always called as signature_decomposition(Module:Arg1, Arg2), so you have to write it to handle this construct properly. In this case I’d probably have a clause

signature_decomposition(M:A, D) :-
    signature_decomposition(A, M, D).

Then the first clause becomes

signature_decomposition({ A }, M, node(def, [])) :- call(M:A).

That is pretty common. It is probably as simple as

term_expansion(A::B, typeDecls:(A::B)).
term_expansion((A::B :- Body), typeDecls:(A::B :- Body)).

If you define that in the file you write the rules you are fine. If you want to use this in many files I’d consider writing a file with all declarations that you can reuse using include/1.

2 Likes

Thanks @jan, that’s getting me closer to the finish line!
Now, I started wondering when prolog decides to return a term qualified with the module. Consider:

typeDecls:(member(A,B) :: pred(T, list(T))) :-
    A :: T, B :: list(T).

and the interaction:

?- clause(member(_, [1,2,3]) :: pred(A, list(A)), Clause).
Clause = user:(_6968::A, [1, 2, 3]::list(A)) .

why is that prefixed with the module user? I think that means user:(,)(_6968::A, [1,2,3]::list(A)) (don’t know how to write the comma as prefix). And maybe it makes sense: the conjunction of that predicate lives indeed in user. But then, why not decorating the subterms also? Why isn’t the answer:

Clause = user:(typeDecls(_6968::A), typeDecls:([1, 2, 3]::list(A))) .

I also discovered I can “solve” this problem with strip_module. So:

?- clause(member(_, [1,2,3]) :: pred(A, list(A)), Clause), strip_module(Clause, _, Clause1).
Clause = user:(_246::A, [1, 2, 3]::list(A)),
Clause1 =  (_246::A, [1, 2, 3]::list(A)) .

but I’d still like to understand where prolog decides to prefix with module informations.

Not entirely sure. I doubt clause/2 always does the correct thing. If however the above clause was declared in the context of user for the predicate that lives in typeDecls, qualifying the body using user makes sense. The assumption for the body of clause/2 is (I think) that the body lives in the same module as the predicate (head). If this is not the case, the body is qualified. So, this happens if a clause is added to a multifile predicate in another module only qualifying the head.

That’s exactly what was happening! In the end, with your hints, I was able to produce some code I’m happy with. Thanks a lot! In closing, which resources would you suggest to understand the nooks and crannies of swi’s module system, other than this tutorial https://chiselapp.com/user/ttmrichter/repository/gng/doc/trunk/output/tutorials/swiplmodtut.html and the documentation on the swi website?

I fear the ones mentioned are as good as it gets :frowning: Well, you can also learn from the libraries …

Are you aware that this site has a nice feature in that if you write the predicate indicator as text, it will be converted into a link to the documentation.

So instead of writing “strip_module” if you write strip_module/3 it will get converted to strip_module/3. :slightly_smiling_face: