How to parameterize predicate with truth value

Hello,

I’d like to write a kind test predicate that takes as an argument a predicate and whether it is supposed to be true of false.

E.g.
test(X, Truth)

if call(X) succeeds and Truth = true, then test succeeds, and if call(X) fails and Truth=false, it succeeds as well.

I am thinking about two auxiliary predicates, but is there a more compact way to do this.

Edit:

Right now i did it this way:

aux_call(X, true) :-
     !,
    call(X).

aux_call(X, false) :-
    \+ call(X).

test(X, Truth) :-
    aux_call(X, Truth).

I guess, this is pretty compact :slight_smile:

library(reif) is based on such an idea. To reify the truth value.

http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/swi/reif.pl

But I am not sure whether it has a general reifier. You have to check.

Otherwise, the simplest is this negation as failure:

reify(X, T) :- call(X), !, T=true.
reify(_, T) :- T=false.

But it wont give you T uninstantiated, which would harness the real power of library(reif).

1 Like

Thanks for checking.

My need is currently less broad – I only allow Truth to be ground.

Dan

The shortest of the shortest is possibly:

reify(X, T) :- (X -> T=true; T=false).

This is also a nice inlining candidate.

1 Like

Now I had another case:

Instead of truth values that are expected, to test whether a call should cause an exception to occur.

e.g.

test_exception(X, true) – X, when called will cause an exception to occur

test_exception(X, false) – X, when called will not cause an exception to occur

This require playing with catch/3 … after quite a bit of tinkering I came up with the following hack.

With a bit more hacking I could probably also indicate the expected exception name:

test_with_exception(X, true) :-
	!, 
	catch(call(X), _Y, p_recover_true).
		
test_with_exception(X, false) :-
	catch(call(X), _Y, p_recover_false(R)),
	!,
	ground(R).
	
p_recover_true :-
	fail.
p_recover_false(false).

Are you somehow trying to re-implement unit testing? Have you seen this: https://www.swi-prolog.org/pldoc/doc_for?object=section('packages/plunit.html')

Here in particular is an example: https://www.swi-prolog.org/pldoc/man?section=testerror

In the example above, it shows how to do it with or without the unit test harness.

Thanks Boris,

Yes, indeed, it did occur to me that i am doing what is also implemented (somehow) in unit testing.

But, its intended for my “application” level, and adapted to the Domain Specific Language I am working on.

Essentially, I want the user of the DSL to be able to create assertion statements that are then called while the DSL is processed.

So, its not in context of unit testing, but more of an assurance device to the DSL user.

Dan

Have you looked at how PlUnit implements the tests? Maybe you can reuse some of the code or the logic?

1 Like

Shouldn’t test_with_exception(faile, false) succeed?

Try using ignore/1 instead of call/1.

Good point – i didn’t think about this.

Usually, X is expected to succeed or to raise an exception – fail is not in scope.

But, i guess, the general case would include fail without exception as well – which would / could count as success.

Btw, since those calls can have side effects i am also wrapping them into a snapshot predicate – to ensure that any side effects are removed.

however, now i noticed that inside i have transaction – so, now i better check if a snapshot … transaction nesting – works to remove changes committed by the “inner” transaction that might succeed.

Dan

Instead of ignore/1 you can also use this 3-way idiom, heavily used by SWI-Prolog code:

(   catch(X, E, true)
 ->  (   var(E)
     ->  /* success */
     ;   /* error */
     )
 ;   /* failure */
 ).

I think the above works, since the ISO core standard forbids throw/1 with a variable argument.

But it cuts away the choice points of X.

1 Like

Interestingly, my code does generate an anonymous variable within the E structure – making var succeed at all time. I am using domain_error to throw the exception – need to see why it generates such a unbound thing in E …

Dan

calling domain_error generates an E such as the one above. But, such a term structure makes var always succeed and ground always fail.

I had such a ground/var variant solution before but then went away from it because of this …

Yes, exactly …

I thought that ground and var are opposites and when ground didn’t work due to the anonymous variable in the error structure – I assumed that var will also not work.

Thanks for clarifying this …

Dan