Predicate scoping and PL-Unit

I’m currently working on a forward chaining inference engine. It uses a natural language like representation for facts and rules, implemented with Prolog operators (similarly to Bratko: “Prolog Programming for Artificial Intelligence”).

So far so good, the inference engine works as expected.

The problem I’m facing is with integration tests using PL-Unit. When I’m testing the inference engine, I would like to provide the facts and rules from the tests. However, it seems that although the operators are provided correctly to the Pl-Unit module scope, I keep getting this **Unknown procedure** error, since in the inference engine there are no defined predicates corresponding to those operators.

I wrote an example piece of code for this problem. The ‘greets’ operator is defined for demonstration.

:- module(scope,[ op(700,xfy,greets), say_hello/0 ]).

say_hello :-
    X greets Y,
    writef('%w says hello to %w', [X,Y]).

:- begin_tests(say_hello).

    alice greets bob.

    test(say_hello) :-
        say_hello.

:- end_tests(say_hello).

Welcome to SWI-Prolog (threaded, 64 bits, version 8.2.3)
…
?- run_tests.
% PL-Unit: say_hello
**ERROR: scope.pl:13:**
**test say_hello: received error: scope:say_hello/0: Unknown procedure: scope:(greets)/2**
done
% 1 test failed
% 0 tests passed
**false.**

If you move alice greets bob. to the main module, the test passes, but I would naturally like to provide it from the test module. How should I construct the tests?

1 Like

Thanks for the clear example. The behaviour you observe is correct. The unit test box is technically a submodule of the enclosing module, i.e., the test module sees what is in the entailed module, but not the other way around. It isn’t really clear to me what is ultimately intended. It seems you want scope to declare a predicate say_hello/0 that should depend on a greets/2 defined elsewhere. That turns say_hello/0 into a meta predicate. Without arguments that is a bit hairy, but this should work:

:- module_transparent say_hello/0.

say_hello :-
    context_module(M),
    M:(X greets Y).

This is a bit unrealistic example. See meta_predicate/1 for more info. module_transparent/0 should be avoided if meta_predicate/1 is also applicable

Thank you for your help, Jan, I really appreciate it.

The solution you suggested solves the problem. However, I find that I would need to introduce the context module as an argument to be passed around to all predicates in the inference module, in order to be able to access the predicates defined in the test module.

This would be a not-so-elegant solution, so I kept on reading other posts on this forum and stumbled into this one If I understand correctly, Eric had a similar problem a year ago.

Your comment in the thread was “As is, this has no clean solution in the plain SWI-Prolog module system.”. Anyway, I will keep on studying along the lines you suggested, and try to find out if meta predicates could be used.

2 Likes