Suppose I have a predicate foo/2 which defines a relation between its first and second argument.
What is the most idiomatic and efficient way to change the implementation of foo/2 such that:
if both of its arguments are ground, it acts as before (succeeds if the relation holds, fails otherwise).
if one of the two arguments (or both) are free, it "constrains" those two arguments so that when they will get grounded, the relation will be checked.
In other words, how to correctly implement the behaviour exhibited by dif/2 but with any kind of user-defined relation?
listing(dif/2). was of little help.
Different Prolog implementations provide different features to accomplish this. The mechanism is variously known as coroutining, delayed goals, constraints, and your Prolog system's manual will provide more information.
Here are two variants, which are available in SICStus Prolog and also some other systems.
block/1 directive
In SICStus Prolog (and possibly some other systems), one way to lift a user-defined predicate to such a constrained version is available via the declarative block declaration.
Interestingly, this does not require any changes to the predicate itself!
Suppose you have an impure version of dif/2, using the non-monotonic (\=)/2 predicate:
madif(X, Y) :-
X \= Y.
Then you can turn it into a delayed version for example with:
:- block madif(-, ?),
madif(?, -).
madif(X, Y) :-
X \= Y.
Sample queries and answers:
| ?- madif(a, b).
yes
| ?- madif(a, X).
user:madif(a,X) ? ;
no
| ?- madif(a, X), X = b.
X = b ? ;
no
| ?- madif(X, Y).
user:madif(X,Y) ? ;
no
As required, the evaluation of the goal is delayed until both arguments are instantiated.
when/2
A second way to accomplish this with SICStus Prolog (and other systems that provide this feature) is to use when/2. This requires changes to the predicate itself.
For example, using when/2, you can implement madif/2 like this:
madif(X, Y) :-
when((ground(X),
ground(Y)), X \= Y).
Sample queries and answers:
| ?- madif(X, a).
prolog:trig_ground(X,[],[X],_A,_A),
prolog:when(_A,(ground(X),ground(a)),user:(X\=a)) ? ;
no
| ?- madif(X, a), X = b.
X = b ? ;
no
First and foremostly,
Take the user's viewpoint
... and not that of an implementer. All too often this is ignored – also in existing constraint implementations. And it shows. So here are the most salient aspects to take into account.
Correctness
Obviously this should hold. It is always better to produce clean errors, mostly instantiation errors, better to flounder forever, even better to loop forever than to fail incorrectly. If all else breaks you can wrap your attempt with freeze(_, G_0). Note that you do need a working toplevel to actually see such floundering goals. SICStus has such a toplevel1, in SWI you need to wrap your query as call_residue_vars(Query_0, Vs) to see all attached constraints.
Consistency
Next you want to ensure that your constraint ensures consistency as much as possible. There are many notions of consistency like, domain and bounds consistency. To take your precise requirement think of difgrn/2 and compare it to the built-in dif/2:
difgrn(X, Y) :-
when((ground(X), ground(Y)), X \== Y).
| ?- difgrn(X, X).
prolog:trig_ground(X,[],[X],_A,_B),
prolog:trig_ground(X,[],[X],_A,_C),
prolog:trig_and(_C,[],_A,_B,_A),
prolog:when(_A,(ground(X),ground(X)),user:(X\==X)) ? ;
no
| ?- dif(X, X).
no
| ?- difgrn([], [_]).
prolog:trig_ground(_A,[],[_A],_B,_C),
prolog:trig_and(_C,[],_B,1,_B),
prolog:when(_B,(ground([]),ground([_A])),user:([]\==[_A]))
| ?- dif([], [_]).
yes
One way to implement dif/2 in full strength is to use the very special condition (?=)/2:
difwh(X,Y) :- when(?=(X,Y), X\==Y).
which should answer your question as best as one can:
In other words, how to correctly implement the behaviour exhibited by dif/2 but with any kind of user-defined relation?
But unfortunately, this does not extend to anything else.
The situation becomes even more complex if one considers consistency between various constraints. Think of X in 1..2, dif(X, 1), dif(X, 2).
Answer projections
(For lack of a better word.) Sometimes you want to see your constraints nicely on the toplevel - and the best way is to represent them as goals that themselves will reestablish the exact state required to represent an answer.
See above trig_ground answers, which certainly could be beautified a bit.
Variable projections
Same as answer projections but possible at any point in time, via frozen/2 or copy_term/3.
Subsumption checking
This is useful for diagnostic purposes and loop checks.
For purely syntactic terms, there is subsumes_term/2 which ignores constraints. A prerequisite to perform an effective test is to connect each involved variable to the actual constraint. Consider the goal freeze(X, Y = a) and imagine some subsumption checking with Y as an argument. If Y is no longer attached to the information (as it usually is with current implementations of freeze/2) you will come to the wrong conclusion that this Y subsumes b.
Note as for the actual example of dif/2, this was the very first constraint ever (1972, Prolog 0). A more elaborate description gives Michel van Caneghem in L'anatomie de Prolog, InterÉditions 1986 and Lee Naish in Papers about MU-Prolog.
1 Half-true. For library(clpfd) you need assert(clpfd:full_answer).
Related
I'm working through Clocksin and Mellish to try and finally go beyond just dabbling in Prolog. FWIW, I'm running SWI-Prolog:
SWI-Prolog version 7.2.3 for x86_64-linux
Anyway, I implemented a diff/2 predicate as part of exercise 1.4. The predicate is very simple:
diff(X,Y) :- X \== Y.
And it works when used in the sister_of predicate, like this:
sister_of(X,Y) :-
female(X),
diff(X,Y),
parents(X, Mum, Dad ),
parents(Y, Mum, Dad ).
in that, assuming the necessary additional facts, doing this:
?- sister_of(alice,alice).
returns false as expected. But here's the rub. If I do this instead:
?- sister_of(alice, Who).
(again, given the additional facts necessary)
I get
Who = edward ;
Who = alice;
false
Even though, as already shown, the sister_of predicate does not treat alice as her own sister.
On the other hand, if I use the SWI provided dif/2 predicate, then everything works the way I would naively expect.
Can anyone explain why this is happening this way, and why my diff implementation doesn't work the way I'm expecting, in the case where I ask for additional unifications from that query?
The entire source file I'm working with can be found here
Any help is much appreciated.
As you note, the problem stems from the interplay between equality (or rather, inequality) and unification. Observe that in your definition of sister_of, you first find a candidate value for X, then try to constrain Y to be different, but Y is still an uninstantiated logic variable and the check is always going to succeed, like diff(alice, Y) will. The following constraints, including the last one that gives a concrete value to Y, come too late.
In general, what you need to do is ensure that by the time you get to the inequality check all variables are instantiated. Negation is a non-logical feature of Prolog and therefore potentially dangerous, but checking whether two ground terms are not equal is safe.
I got a database that looks like
hasChild(person1, person2).
hasChild(person1, person3).
hasChild(person4, person5).
Which means that (for example) person1 has child named person2.
I then create a predicate that identifies if the person is a parent
parent(A):- hasChild(A,_).
Which identifies if the person is a parent, i.e. has any children
Then I try to create a predicate childless(A) that should return true if the user doesn't have any children which is basically an inverse of parent(A).
So I have 2 questions here:
a) is it possible to somehow take an "inverse" of a predicate, like childless(A):-not(parent(A)). or in any other way bypass this using the hasChild or any other method?
b) parent(A) will return true multiple times if the person has multiple children. Is it possible to make it return true only once?
For problem 1, yes. Prolog is not entirely magically delicious to some because it conflates negation and failure, but you can definitely write:
childless(X) :- \+ hasChild(X, _).
and you will see "true" for people that do not have children. You will also see "true" for vegetables, minerals, ideologies, procedures, shoeboxes, beer recipes and unfeathered bipeds. If this is a problem for you, a simple solution is to improve your data model, but complaining about Prolog is a very popular alternative. :)
For problem 2, the simplest solution is to use once:
parent(A) :- once(hasChild(A, _)).
This is a safer alternative to using the cut operator, which would look like this:
parent(A) :- hasChild(A, _), !.
This has a fairly significant cost: parent/1 will only generate a single valid solution, though it will verify other correct solutions. To wit:
?- parent(X).
X = person1.
Notice it did not suggest person4. However,
?- childless(person4).
true.
This asymmetry is certainly a "code smell" to most any intermediate Prolog programmer such as myself. It's as though Prolog has some sort of amnesia or selective hearing depending on the query. This is no way to get invited to high society events!
I would suggest that the best solution here (which handles the mineral/vegetable problem above as well) is to add some more facts about people. After all, a person exists before they have kids (or do they?) so they are not "defined" by that relationship. But continuing to play the game, you may be able to circumvent the problem using setof/3 to construct a list of all the people:
parent(Person) :-
setof(X, C^hasChild(X, C), People),
member(Person, People).
The odd expression C^hasChild(X, C) tells Prolog that C is a free variable; this ensures that we get the set of all things in the first argument of hasChild/2 bound to the list People. This is not first-order logic anymore folks! And the advantage here is that member/2 will generate for us as well as check:
?- parent(person4).
true.
?- parent(X).
X = person1 ;
X = person4.
Is this efficient? No. Is it smart? Probably not. Is it a solution to your question that also generates? Yes, it seems to be. Well, one out of three ain't bad. :)
As a final remark, some Prolog implementations treat not/1 as an alias for \+/1; if you happen to be using one of them, I recommend you not mistake compatibility with pre-ISO conventions for a jovial tolerance for variety: correct the spelling of not(X) to \+ X. :)
Here's another way you could do it!
Define everything you know for a fact as a Prolog fact, no matter if it is positive or negative.
In your sample, we define "positives" like person/1 and "negatives" like childless/1. Of course, we also define the predicates child_of/2, male/1, female/1, spouse_husband/2, and so on.
Note that we have introduced quite a bit of redundancy into the database.
In return, we got a clearer line of knowns/unknowns without resorting to higher-order constructs.
We need to define the right data consistency constraints:
% There is no person which is neither male nor female.
:- \+ (person(X), \+ (male(X) ; female(X))).
% Nobody is male and female (at once).
:- \+ (male(X), female(X)).
% Nobody is childless and parental (at once).
:- \+ (childless(X), child_of(_,X)).
% There is no person which is neither childless nor parental.
:- \+ (person(X), \+ (childless(X) ; child_of(_,X))).
% There is no child which is not a person.
:- \+ (child_of(X,_), \+ person(X)).
% There is no parent which is not a person.
:- \+ (child_of(_,X), \+ person(X)).
% (...plus, quite likely, a lot more integrity constraints...)
This is just a rough sketch... Depending on your use-cases you could do the modeling differently, e.g. using relations like parental/1 together with suitable integrity constraints. YMMY! HTH
I'm new to prolog; I'm coming from a structured programming background, as will become obvious :)
I am building up a prolog query that involves reversing a number; eg. reverse_num(123,X) results in X = 321. I came up with the following definition, but it only works when I provide a number as the first parameter.
reverse_num(Num, Revnum) :-
number_chars(Num, Atoms),
reverse(Revatoms, Atoms),
number_chars(Reversed, Revatoms),
Reversed = Revnum.
the number_chars/2 predicate doesn't like an unsubstantiated variable if I do: reverse_num(X,123) (where I'm expecting X to be 321).
Am I trying too hard to make reverse_num do something it shouldn't (should it be understood to work only with a number as the first parameter and variable as the second)?
Or is there an easy / straight-forward way to handle a variable as the first parameter?
Relational naming
Before jumping into coding, let's take a step back. After all, the idea in Prolog is to define relations. Your name reverse_num/2 rather suggests some actions, num_reversed/2 might be a better name.
Determine the relation
Your definition is not that bad, let me rewrite it to1:
num_reversed(Num, Reversed) :-
number_chars(Num, Chars),
reverse(Chars, Revchars),
number_chars(Reversed, Revchars).
?- num_reversed(123,X).
X = 321.
?- num_reversed(1230,X).
X = 321.
?- num_reversed(12300,X).
X = 321.
Do you see the pattern? All numbers N*10^I have the same result!
Now, let's ask some more:
?- num_reversed(Num, 321).
error(instantiation_error,number_chars/2).
Hm, what did we expect? Actually, we wanted all 123*10^I to be printed. That's infinitely many solutions. So above query, if correctly answered, would require infinitely many solutions to be printed. If we print them directly, that will take all our universe's lifetime, and more!
It is for this reason, that Prolog produces an instantiation error instead. By this, Prolog essentially states:
This goal is too general that I can make a good answer. Maybe there are infinitely many solutions, maybe not. I know not. But at least I indicate this by issuing an error. To remove this error you need to instantiate the arguments a bit more.
So the answer Prolog produced was not that bad at all! In fact, it is much better to produce a clean error than to, say, fail incorrectly. In general, Prolog's errors are often a very useful hint to what semantic problems you might have. See all error classes how.
Coroutining
As have other answers suggested, coroutining, using when/2 might solve this problem. However, coroutining itself has many semantic problems. Not without reason, systems like XSB do not offer it, due to the many problems related to subsumption checking. An implementation that would be compatible to it would be unexpectedly inefficient.
But for the sake of the point, we could make our definition more versatile by querying it like
?- when(nonvar(Num), num_reversed(Num, Reversed)).
when(nonvar(Num), num_reversed(Num, Reversed)).
Now we get back as an answer exactly the query we entered. This is also known as floundering. So there is a way to represent infinitely may solutions in a compact manner! However, this comes at a rather high price: You no longer know whether a solution exists or not. Think of:
?- when(nonvar(Num), num_reversed(Num, -1)).
when(nonvar(Num), num_reversed(Num, -1)).
Others have suggested to wait also for nonvar(Reversed) which would only be correct if we would produce infinitely many answers - but, as we have seen - this just takes too much time.
Coroutining looked as a very promising road at the beginning of the 1980s. However, it has never really caught on as a general programming methodology. Most of the time you get much too much floundering which is just a pain and even more difficult to handle than, say instantiation errors.
However, a more promising offspring of this development are constraints. There, the mechanisms are much cleaner defined. For practical purposes, programmers will only use existing libraries, like CLPFD, CLPQ, or CHR. Implementing your own library is an extremely non-trivial project in its own right. In fact it might even be possible to provide an implementation of num_reversed/2 using library(clpfd) that is, restricting the relation to the integer case.
Mode dependent conditionals
Traditionally, many such problems are solved by testing for instantiations explicitly. It is good style to perform this exclusively with nonvar/1 and ground/1 like the condition in when/2- other type test predicates lead easily to errors as exemplified by another answer.
num_reversed(Num, Reversed) :-
( nonvar(Num)
-> original_num_reversed(Num, Reversed)
; original_num_reversed(Reversed, Base),
( Base =:= 0
-> Num is 0
; length(_, I),
Num is Base*10^I
)
).
Above code breaks very soon for floats using base 2 and somewhat later for base 10. In fact, with classical base 2 floats, the relation itself does not make much sense.
As for the definition of number_chars/2, ISO/IEC 13211-1:1995 has the following template and mode subclause:
8.16.7.2 Template and modes
number_chars(+number, ?character_list)
number_chars(-number, +character_list)
The first case is when the first argument is instantiated (thus nonvar). The second case, when the first argument is not instantiated. In that case, the second argument has to be instantiated.
Note, however, that due to very similar problems, number_chars/2 is not a relation. As example, Chs = ['0','0'], number_chars(0, Chs) succeeds, whereas number_chars(0, Chs), Chs = ['0','0'] fails.
Very fine print
1 This rewrite is necessary, because in many Prologs reverse/2 only terminates if the first argument is known. And in SWI this rewrite is necessary due to some idiosyncratic inefficiencies.
The number_chars/2 predicate has the signature:
number_chars(?Number, ?CharList)
But although not fully specified by the signature, at least Number or CharList have to be instantiated. That's where the error occurs from.
If you call:
reverse_num(Num,123)
You will call number_chars/2 with both uninstatiated at that time so the predicate will error.
A not very nice solution to the problem is to ask whether Num or RevNum are number/2s. You can do this by writing two versions. It will furthermore filter other calls like reverse_num(f(a),b), etc.:
reverse_num(Num,Revnum) :-
\+ number(Num),
\+ number(Revnum),
throw(error(instantiation_error, _)).
reverse_num(Num, Revnum) :-
ground(Num),
number(Num),
!,
number_chars(Num, Atoms),
reverse(Revatoms, Atoms),
number_chars(Revnum, Revatoms).
reverse_num(Num, Revnum) :-
ground(Revnum),
number(Revnum),
reverse_num(Revnum,Num).
Or you can in case you use two nongrounds (e.g. reverse_num(X,Y).) an instantiation error instead of false as #false says:
reverse_num(Num,Revnum) :-
\+ number(Num),
\+ number(Revnum),
!,
throw(error(instantiation_error, _)).
reverse_num(Num, Revnum) :-
number(Num),
!,
number_chars(Num, Atoms),
reverse(Revatoms, Atoms),
number_chars(Revnum, Revatoms).
reverse_num(Num, Revnum) :-
reverse_num(Revnum,Num).
The cut (!) is not behaviorally necessary, but will increase performance a bit. I'm not really a fan of this implementation, but Prolog cannot always fully make predicates reversible since (a) reversibility is an undecidable property because Prolog is Turing complete; and (b) one of the characteristics of Prolog is that the body atoms are evaluated left-to-right. otherwise it will take ages to evaluate some programs. There are logic engines that can do this in an arbitrary order and thus will succeed for this task.
If the predicate/2 is commutative, a solution that can be generalized is the following pattern:
predicate(X,Y) :-
predicate1(X,A),
predicate2(A,B),
% ...
predicaten(C,Y).
predicate(X,Y) :-
predicate(Y,X).
But you cannot simply add the last clause to the theory, because it can loop infinitely.
Nice to see someone is also worried about define flexible rules with no restrictions in the set of bound arguments.
If using a Prolog system that supports coroutining and the when/2 built-in predicate (e.g. SICStus Prolog, SWI-Prolog, or YAP), try as:
reverse_num(Num, Reversed) :-
when( ( ground(Num); ground(Atoms) ), number_chars(Num, Atoms) ),
when( ( ground(Reversed); ground(Revatoms) ), number_chars(Reversed, Revatoms) ),
reverse(Atoms , Revatoms).
that gives:
?- reverse_num( 123, X ).
X = 321.
?- reverse_num( X, 123 ).
X = 321 .
( thanks to persons who provided theses answers: Prolog: missing feature? )
This SWISH session shows my effort to answer.
Then I've come back here, where I found I was on #PasabaPorAqui' mood (+1), but I didn't get it right.
But, such an interesting topic: notice how regular is the join pattern.
reverse_num(X, Y) :-
when((nonvar(Xs);nonvar(Ys)), reverse(Xs, Ys)),
when((nonvar(X) ;nonvar(Xs)), atomic_chars(X, Xs)),
when((nonvar(Y) ;nonvar(Ys)), atomic_chars(Y, Ys)).
So, we can generalize in a simple way (after accounting for PasabaPorAqui correction, ground/1 it's the key):
% generalized... thanks Pasaba Por Aqui
:- meta_predicate when_2(0).
when_2(P) :-
strip_module(P,_,Q),
Q =.. [_,A0,A1],
when((ground(A0);ground(A1)), P).
reverse_num(X, Y) :-
maplist(when_2, [reverse(Xs, Ys), atomic_chars(X, Xs), atomic_chars(Y, Ys)]).
I think I understand why nonvar/1 was problematic: the list bound for reverse get 'fired' too early, when just the head get bound... too fast !
maplist/2 is not really necessary: by hand we can write
reverse_num(X, Y) :-
when_2(reverse(Xs, Ys)),
when_2(atomic_chars(X, Xs)),
when_2(atomic_chars(Y, Ys)).
this seems an ideal application of term rewriting... what do you think about -:- ? Implementing that we could write bidirectional code like
reverse_num(X, Y) -:-
reverse(Xs, Ys),
atomic_chars(X, Xs),
atomic_chars(Y, Ys).
edit SWISH maybe is not 'term_rewrite' friendly... so here is a lower level approach:
:- op(900, xfy, ++).
A ++ B ++ C :- when_2(A), B ++ C.
A ++ B :- when_2(A), when_2(B).
reverse_num(X, Y) :-
reverse(Xs, Ys) ++ atomic_chars(X, Xs) ++ atomic_chars(Y, Ys).
Setting aside the problem of trailing zeroes turning into leading zeroes, it doesn't seem like it should be much more complicated than something like this (made somewhat more complicated by dealing with negative numbers):
reverse_number(X,Y) :- number(X) , ! , rev(X,Y) .
reverse_number(X,Y) :- number(Y) , ! , rev(Y,X) .
rev(N,R) :-
N < 0 ,
! ,
A is abs(N) ,
rev(A,T) ,
R is - T
.
rev(N,R) :-
number_chars(N,Ns) ,
reverse(Ns,Rs) ,
number_chars(R,Rs)
.
Note that this does require at least one of the arguments to reverse_number/2 to be instantiated.
The matter of deterministic success of some Prolog goal has turned up time and again in—at least—the following questions:
Reification of term equality/inequality
Intersection and union of 2 lists
Remove duplicates in list (Prolog)
Prolog: How can I implement the sum of squares of two largest numbers out of three?
Ordering lists with constraint logic programming)
Different methods were used (e.g., provoking certain resource errors, or looking closely at the exact answers given by the Prolog toplevel), but they all appear somewhat ad-hack to me.
I'm looking for a generic, portable, and ISO-conformant way to find out if the execution of some Prolog goal (which succeeded) left some choice-point(s) behind. Some meta predicate, maybe?
Could you please hint me in the right direction? Thank you in advance!
Good news everyone: setup_call_cleanup/3 (currently a draft proposal for ISO) lets you do that in a quite portable and beautiful way.
See the example:
setup_call_cleanup(true, (X=1;X=2), Det=yes)
succeeds with Det == yes when there are no more choice points left.
EDIT: Let me illustrate the awesomeness of this construct, or rather of the very closely related predicate call_cleanup/2, with a simple example:
In the excellent CLP(B) documentation of SICStus Prolog, we find in the description of labeling/1 a very strong guarantee:
Enumerates all solutions by backtracking, but creates choicepoints only if necessary.
This is really a strong guarantee, and at first it may be hard to believe that it always holds. Luckily for us, it is extremely easy to formulate and generate systematic test cases in Prolog to verify such properties, in essence using the Prolog system to test itself.
We start with systematically describing what a Boolean expression looks like in CLP(B):
:- use_module(library(clpb)).
:- use_module(library(lists)).
sat(_) --> [].
sat(a) --> [].
sat(~_) --> [].
sat(X+Y) --> [_], sat(X), sat(Y).
sat(X#Y) --> [_], sat(X), sat(Y).
There are in fact many more cases, but let us restrict ourselves to the above subset of CLP(B) expressions for now.
Why am I using a DCG for this? Because it lets me conveniently describe (a subset of) all Boolean expressions of specific depth, and thus fairly enumerate them all. For example:
?- length(Ls, _), phrase(sat(Sat), Ls).
Ls = [] ;
Ls = [],
Sat = a ;
Ls = [],
Sat = ~_G475 ;
Ls = [_G475],
Sat = _G478+_G479 .
Thus, I am using the DCG only to denote how many available "tokens" have already been consumed when generating expressions, limiting the total depth of the resulting expressions.
Next, we need a small auxiliary predicate labeling_nondet/1, which acts exactly as labeling/1, but is only true if a choice-point still remains. This is where call_cleanup/2 comes in:
labeling_nondet(Vs) :-
dif(Det, true),
call_cleanup(labeling(Vs), Det=true).
Our test case (and by this, we actually mean an infinite sequence of small test cases, which we can very conveniently describe with Prolog) now aims to verify the above property, i.e.:
If there is a choice-point, then there is a further solution.
In other words:
The set of solutions of labeling_nondet/1 is a proper subset of that of labeling/1.
Let us thus describe what a counterexample of the above property looks like:
counterexample(Sat) :-
length(Ls, _),
phrase(sat(Sat), Ls),
term_variables(Sat, Vs),
sat(Sat),
setof(Vs, labeling_nondet(Vs), Sols),
setof(Vs, labeling(Vs), Sols).
And now we use this executable specification in order to find such a counterexample. If the solver works as documented, then we will never find a counterexample. But in this case, we immediately get:
| ?- counterexample(Sat).
Sat = a+ ~_A,
sat(_A=:=_B*a) ? ;
So in fact the property does not hold. Broken down to the essence, although no more solutions remain in the following query, Det is not unified with true:
| ?- sat(a + ~X), call_cleanup(labeling([X]), Det=true).
X = 0 ? ;
no
In SWI-Prolog, the superfluous choice-point is obvious:
?- sat(a + ~X), labeling([X]).
X = 0 ;
false.
I am not giving this example to criticize the behaviour of either SICStus Prolog or SWI: Nobody really cares whether or not a superfluous choice-point is left in labeling/1, least of all in an artificial example that involves universally quantified variables (which is atypical for tasks in which one uses labeling/1).
I am giving this example to show how nicely and conveniently guarantees that are documented and intended can be tested with such powerful inspection predicates...
... assuming that implementors are interested to standardize their efforts, so that these predicates actually work the same way across different implementations! The attentive reader will have noticed that the search for counterexamples produces quite different results when used in SWI-Prolog.
In an unexpected turn of events, the above test case has found a discrepancy in the call_cleanup/2 implementations of SWI-Prolog and SICStus. In SWI-Prolog (7.3.11):
?- dif(Det, true), call_cleanup(true, Det=true).
dif(Det, true).
?- call_cleanup(true, Det=true), dif(Det, true).
false.
whereas both queries fail in SICStus Prolog (4.3.2).
This is the quite typical case: Once you are interested in testing a specific property, you find many obstacles that are in the way of testing the actual property.
In the ISO draft proposal, we see:
Failure of [the cleanup goal] is ignored.
In the SICStus documentation of call_cleanup/2, we see:
Cleanup succeeds determinately after performing some side-effect; otherwise, unexpected behavior may result.
And in the SWI variant, we see:
Success or failure of Cleanup is ignored
Thus, for portability, we should actually write labeling_nondet/1 as:
labeling_nondet(Vs) :-
call_cleanup(labeling(Vs), Det=true),
dif(Det, true).
There is no guarantee in setup_call_cleanup/3 that it detects determinism, i.e. missing choice points in the success of a goal. The 7.8.11.1 Description draft proposal only says:
c) The cleanup handler is called exactly once; no later than
upon failure of G. Earlier moments are:
If G is true or false, C is called at an implementation
dependent moment after the last solution and after the last
observable effect of G.
So there is currently no requirement that:
setup_call_cleanup(true, true, Det=true)
Returns Det=true in the first place. This is also reflected in the test cases 7.8.11.4 Examples that the draf proposal gives, we find one test case which says:
setup_call_cleanup(true, true, X = 2).
Either: Succeeds, unifying X = 2.
Or: Succeeds.
So its both a valid implementation, to detect determinism and not to detect determinism.
Having recently got into Prolog I've been using it for a few simple tasks and began to wonder about using member within forall loops like the one in the trivial example below:
forall(member(A,[1,2,3,4]), print(A)).
In the case that you do something like this is it always true that forall will process the elements within the list in the same order every time its called? Does it have to be enforced by say doing something like:
A = [1,2,3,4], sort(A, B), forall(member(C,B), print(C)).
From what little research I've initially done I'm guessing that it comes down to the behaviour of member/2 but the documentation for the function on SWI-Prolog's website is very brief. It does however mention determinism with regards member/2 which gave me an inkling I might be on the right path in saying that it would always extract the elements in the same order, though I'm far from certain.
Can anyone give me any guarantees or explanations on this one?
Non-determinism in Prolog simply refers to a predicate having potentially more than one solution. Clearly, member/2 is such a predicate. This does not mean that you have to be worried about your computation becoming unpredictable. Prolog has a well-defined computation rule which essentially says that alternative solutions are explored in a depth-first, left-to-right manner. Thus your goal member(X,[1,2,3,4]) will generate solutions to X in the expected order 1,2,3,4.
Sorting the list [1,2,3,4] will not make any difference, as it is already sorted (according to Prolog's standard term order).
A word of caution about forall/2: some Prologs define this, but it is probably less useful than you imagine, because it is not really a "loop". You can use it in your example because you only perform a print side effect in each iteration. For most other purposes, you should familiarize yourself with recursive patterns like
print_list([]).
print_list([X|Xs]) :- print(X), print_list(Xs).
Strictly speaking, there is no guarantee in SWI on several levels:
1mo, that member/2 or forall/2 will perform in exactly this manner, since you can redefine them.
?- [user].
member(X,X).
|: % user://1 compiled 0.00 sec, 2 clauses
true.
?- forall(member(A,[1,2,3,4]), print(A)).
[1,2,3,4]
true.
However, member/2 is defined in the Prolog prologue which covers all the details you are interested in.
As for forall(A,B) it is safer to write \+ (A, \+B) instead, since this relies on standard features only. There is no definition of forall/2 as such, so it is difficult to tell what is the "right" behavior.
2do, that SWI will be standard conforming. If you read the documentation, you will note that there is no self-declaration (as for, e.g. SICStus Prolog) for standard conformance. In fact, \+ (A, \+B) is not fully conforming, as in the following example that should silently fail, but rather prints nonconforming
?- \+ ( C= !, \+ (C,fail;writeq(nonconforming))).
N208 has forall/2 defined + (call(Generator), + call(Test)), so this makes it less dubious. But by virtue that the ISO core standard (+)/1 does already a call/1 and that the ISO core standard (,)/2 will be subject to body conversion one can simply define it as follows in an ISO core standard Prolog:
forall(Generator, Test) :-
\+ (Generator, \+ Test).
SWI-Prolog has also implemented this way, and the error observed by Ulrich Neumerkel will not be seen when using forall/2:
Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.18)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
?- \+ ( C= !, \+ (C,fail;writeq(nonconforming))).
nonconforming
true.
?- forall(C=!, (C,fail;writeq(nonconforming))).
false.
Side remark:
I don't know how useful it is for loop. It seems to me using it for loops is not the right approach, since the test might fail, and then the construct also fails. I have also seen by Striegnitz and Blackburn the following definition of a helper predicate that they call failiure driven loop.
doall(Goal) :-
Goal, fail.
doall(_).
I find myself directly writing Goal, fail; true which also does the job:
Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.18)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
?- member(A,[1,2,3,4]), write(A), nl, fail; true.
1
2
3
4
true.