# How to call predicate in test that is called from outside of test?

**URL:** <https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597>\
**Category:** Help!\
**Tags:** many\_worlds\
**Created:** [December 10, 2019, 4:34pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597 "2019-12-10T16:34:16Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [December 10, 2019, 4:34pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597/1 "2019-12-10T16:34:16Z")

</div>

How can a predicate `parent/2` be called if it is created in a test, but the predicate that calls parent/2, (ancestor/2) is outside of the test and the call to `ancestor/2` is initiated by a test?

```prolog
Test module Main module Test module
test -> ancestor/2 -> parent/2

```

* * *

Here is a complete minimal example.

The main module shows a set of predicates (`contains/3` and `ancestor/3`) that work if I pass the module as an argument.

The main module also shows a set of predicates that I would like to use (`contains/2` and `ancestor/2`) which don’t pass the module in an argument.

There are also two separate tests modules that tests these predicates.

`assembly_a` test the predicates in main that pass the module as an argument and succeed.

`assembly_b` test the predicates in main that do not pass the module as an argument and these fail when I would like them to succeed.

> **Click triangle to see minimal example**
>
> ```prolog
> :- module(main, []).
> 
> contains(Module,Assembly,Part_type) :-
> ancestor(Module,Assembly,Part_type), !.
> 
> ancestor(Module,A,B) :-
> Module:parent(A,B).
> ancestor(Module,A,C) :-
> Module:parent(A,B),
> ancestor(Module,B,C).
> 
> contains(Assembly,Part_type) :-
> ancestor(Assembly,Part_type), !.
> 
> ancestor(A,B) :-
> parent(A,B).
> ancestor(A,C) :-
> parent(A,B),
> ancestor(B,C).
> 
> % ---------------------------------------------------------------------------------------------------------------------
> 
> :- begin_tests(assembly_a).
> 
> parent(bike,frame).
> parent(bike,drivechain).
> parent(bike,wheel).
> parent(wheel,spokes).
> parent(wheel,rim).
> parent(wheel,hub).
> parent(drivechain,crank).
> parent(drivechain,pedal).
> parent(drivechain,chain).
> 
> % Test: parent/2 - module verification
> test(01) :-
> predicate_property(parent(_,_), implementation_module(Module)),
> assertion( Module == plunit_assembly_a ).
> 
> % Test: contains/3 - no error - 1 level
> test(02) :-
> contains(plunit_assembly_a,bike,frame).
> 
> % Test: contains/3 - no error - 2 levels
> test(03) :-
> contains(plunit_assembly_a,bike,crank).
> 
> % Test: contains/3 - fail
> test(04,[fail]) :-
> contains(plunit_assembly_a,bike,a).
> 
> :- end_tests(assembly_a).
> 
> % -----------------------------------------------------------------------------
> 
> :- begin_tests(assembly_b).
> 
> :- meta_predicate parent(2,?).
> 
> parent(bike,frame).
> parent(bike,drivechain).
> parent(bike,wheel).
> parent(wheel,spokes).
> parent(wheel,rim).
> parent(wheel,hub).
> parent(drivechain,crank).
> parent(drivechain,pedal).
> parent(drivechain,chain).
> 
> % Test: parent/2 - module verification
> test(01) :-
> predicate_property(parent(_,_), implementation_module(Module)),
> assertion( Module == plunit_assembly_b ).
> 
> % Test: contains/3 - no error - 1 level
> test(02) :-
> contains(bike,frame).
> 
> % Test: contains/3 - no error - 2 levels
> test(03) :-
> contains(bike,crank).
> 
> % Test: contains/3 - fail
> test(04,[fail]) :-
> contains(bike,a).
> 
> :- end_tests(assembly_b).
> 
> ```

I am thinking meta\_predicate/1 should work as given in `assembly_b`,  
`:- meta_predicate parent(2,?).` but it did not.

Previous related post: [How can the module for a term be identified?](https://swi-prolog.discourse.group/t/how-can-the-module-for-a-term-be-identified/1536)

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [December 10, 2019, 6:19pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597/2 "2019-12-10T18:19:35Z")

</div>

Related: [https://stackoverflow.com/questions/57341339/structuring-swi-prolog-code-into-modules-for-unit-testing-for-varying-data-sets/57347452#57347452](https://stackoverflow.com/questions/57341339/structuring-swi-prolog-code-into-modules-for-unit-testing-for-varying-data-sets/57347452#57347452)

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [December 11, 2019, 1:42pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597/3 "2019-12-11T13:42:16Z")

</div>

Thanks to Paulo his response put me on a path that lead to an acceptable solution.

While I did not use any of the answers noted in the referenced StackOverflow question, the answer by @PaulBrownMagic was most helpful in this case.

The only way I found to use `ancestor/2` without adding a module argument to the predicate was to put the `parent/2` predicates in to the same module as `ancestor/2`.

Since hard coding them into the same module as `ancestor/2` would cause a possible conflict of so called different worlds, the idea to use the test setup and cleanup to assert them into the module was used.

To allow the code to dynamically find the name of module for `ancestor/2`, [predicate\_property/2](https://www.swi-prolog.org/pldoc/man?predicate=predicate_property/2) was used.

To suppress the warnings about parent/2 not being defined [dynamic/1](https://www.swi-prolog.org/pldoc/man?predicate=dynamic/1) was added.

> **Click triangle to see working code**
>
> ```prolog
> :- module(main, [
> contains/2,
> ancestor/2
> ]).
> 
> :- dynamic parent/2.
> 
> contains(Assembly,Part_type) :-
> ancestor(Assembly,Part_type), !.
> 
> ancestor(A,B) :-
> parent(A,B).
> ancestor(A,C) :-
> parent(A,B),
> ancestor(B,C).
> 
> :- begin_tests(variation_d).
> 
> path_setup :-
> predicate_property(ancestor(_,_), implementation_module(Module)),
> assert( Module:parent(bike,frame) ),
> assert( Module:parent(bike,drivechain) ),
> assert( Module:parent(bike,wheel) ),
> assert( Module:parent(wheel,spokes) ),
> assert( Module:parent(wheel,rim) ),
> assert( Module:parent(wheel,hub) ),
> assert( Module:parent(drivechain,crank) ),
> assert( Module:parent(drivechain,pedal) ),
> assert( Module:parent(drivechain,chain) ).
> 
> path_cleanup :-
> predicate_property(ancestor(_,_), implementation_module(Module)),
> retractall( Module:parent(_,_) ).
> 
> % Test: ancestor/2 - module verification
> test(01) :-
> predicate_property(ancestor(_,_), implementation_module(Module)),
> assertion( Module == main ).
> 
> % Test: contains/3 - no error - 1 level
> test(02,[setup(path_setup),cleanup(path_cleanup)]) :-
> contains(bike,frame).
> 
> % Test: contains/3 - no error - 2 levels
> test(03,[setup(path_setup),cleanup(path_cleanup)]) :-
> contains(bike,crank).
> 
> % Test: contains/3 - fail
> test(04,[fail,setup(path_setup),cleanup(path_cleanup)]) :-
> contains(bike,a).
> 
> :- end_tests(variation_d).
> 
> ```
> 
> Example run:
> 
> ```prolog
> ?- run_tests.
> % PL-Unit: variation_d .... done
> % All 4 tests passed
> true.
> 
> ```

* * *

EDIT

Of interest:  
[library(persistency): Provide persistent dynamic predicates](https://www.swi-prolog.org/pldoc/man?section=persistency)  
[A Parameterised Module System for Constructing Typed Logic Programs](https://www.ijcai.org/Proceedings/93-2/Papers/006.pdf)  
[More on Transitive Closure](https://www3.cs.stonybrook.edu/~warren/xsbbook/node16.html)

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [December 11, 2019, 1:59pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597/4 "2019-12-11T13:59:21Z")

</div>

> [@EricGT](#):
>
> Since hard coding them into the same module as `ancestor/2` would cause a possible conflict of so called different worlds, the idea to use the test setup and cleanup to assert them into the module was used.

What you have is an example of the “many worlds” design pattern:

> **[The "many worlds" design pattern - Logtalk](https://logtalk.org/2019/11/13/many-worlds-design-pattern.html)**
>
> The many worlds design pattern is one of the most common patterns in Logtalkand Prolog applications. It allows reasoning about different worlds, where aworld...

---

<div class="post-metadata">

**Author:** ![PaulBrownMagic](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/paulbrownmagic/32/211_2.png) [@PaulBrownMagic](https://swi-prolog.discourse.group/u/PaulBrownMagic)\
**Post date:** [December 11, 2019, 2:11pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597/5 "2019-12-11T14:11:52Z")

</div>

So long as you read the discussion too: my answer there is a dirty hack for when you can’t implement the clean solution with Logtalk. When you have many-worlds, Prolog’s modules start to struggle.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [December 11, 2019, 2:22pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597/6 "2019-12-11T14:22:53Z")

</div>

As is, this has no clean solution in the plain SWI-Prolog module system. One possible option is to define ancestor/2 as a meta predicate and import it into the module that provides the parent/2 relation. That would look like

```prolog
:- meta_predicate ancestor(:,?).

ancestor(M:A, B) :-
    ancestor(A,B,M).

ancestor(A,B,M) :-
    M:parent(A,B).
ancestor(A,C,M) :-
    M:parent(A,B),
    ancestor(B, C, M).

```

This is rather ugly. I’d probably go for

```prolog
:- meta_predicate ancestor(2, ?, ?).

ancestor(Rel, A, B) :-
    call(Rel, A, B).
ancestor(Rel, A, C) :-
    call(Rel, A, B),
    ancestor(Rel, B, C).

```

After which you can rename ancestor to something like _transitive\_closure_.

E.g., XSB has parameterized modules. Possibly SWI-Prolog should get these too at some time …

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [December 11, 2019, 2:26pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597/7 "2019-12-11T14:26:53Z")

</div>

> [@jan](#):
>
> XSB has parameterized modules. Possibly SWI-Prolog should get these too at some time …

After finding my answer I went back into the SWI-Prolog [source](https://github.com/SWI-Prolog/swipl-devel) on GitHub looking to see if this was a pattern you used and ran in the [XSB test suite](https://github.com/SWI-Prolog/swipl-devel/tree/e3aa6425e284d012f8aee70688adc4f595a1b48c/src/Tests/xsb)

Now knowing about parameterized modules in there will take a much closer look. Thanks. 😃

---

<div class="post-metadata">

**Author:** ![pmoura](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/pmoura/32/11_2.png) [@pmoura](https://swi-prolog.discourse.group/u/pmoura)\
**Post date:** [December 11, 2019, 2:37pm UTC](https://swi-prolog.discourse.group/t/how-to-call-predicate-in-test-that-is-called-from-outside-of-test/1597/8 "2019-12-11T14:37:33Z")

</div>

> [@jan](#):
>
> E.g., XSB has parameterized modules. Possibly SWI-Prolog should get these too at some time …

XSB parameterized modules are a limited implementation of +20 years old parametric entity ideas that are found in several systems, including Logtalk. An overview of those ideas and systems can be found in:

[Programming Patterns for Logtalk Parametric Objects](http://dx.doi.org/10.1007/978-3-642-20589-7_4). Paulo Moura. Applications of Declarative Programming and Knowledge Management, Lecture Notes in Artificial Intelligence Vol. 6547, April 2011
