As has been noted by @jnmonette, there is a functional dependency from the first to the second argument. With a query like
?- len2(L, N), dif(N, M), len2(L, M).
L = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J|...], N = 9999, M = 10000
; L = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J|...], N = 10000, M = 9999
; loops.
the error in len2/2
can be detected. After all, L
cannot have two different lengths. Additionally,
?- len2([_|L], N), len2(L, N).
N = 10000, L = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J|...]
; loops.
identifies the error going into the other direction. The lengths of L
and [_|L]
cannot be the same. This can be generalized to cover all such errors:
?- len2(L, N), phrase(([_],...), L,K), len2(K, N).
L = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J|...], N = 10000, K = [_B,_C,_D,_E,_F,_G,_H,_I,_J,_K|...]
; loops.
So far we have used Prolog directly. We were able to state general properties by posing this queries. However, we will only find counterexamples should there be some, but otherwise we are left in the uncertain. And, we were pretty lucky that the actual definition enumerates answers in a fair manner letting each and every answer appear in finite time. In case of a more demanding definition (think of exchanging the order of clauses in len/2
) simply add length(L, _)
in front of all queries so far.
However, in case of a running query we inevitably ask: Should we continue to wait for an answer, or can we already abort the query? After all, for a correct implementation the query will not produce any answer and thus loop indefinitely.
There is no way (in current implementations) to delegate such a query at least into the background running there with lower priority. And therefore such queries are not used for testing at all.
On the other hand, such queries are a very powerful way to express many testable properties. For example many of the bugs in clpfd-systems of SICStus, SWI, and Scryer have been identified in this manner using condor. The cruder support however does not lead to very elegant solutions.
To start to address this problem, the following annotation may help:
:/-& len2(L, N), dif(N, M), len2(L, M).
:/-& len2(L, N), phrase(([_],...), L,K), len2(K, N).
The :/-
means there is no solution - similarly to :- \+ Q_0.
and the additional &
, an asciified ∞, meaning Prolog's execution will be infinite. This annotation therefore leaves room to try out better strategies that disprove that there is no solution.
In GUPU this annotation is executed as a Prolog goal with a (relatively short) timeout. Also alternate strategies are tried in particular iterative deepening which in this case do time out as well. So effectively, the error remains undetected. But with more resources or a better strategy the error might be discovered.
len2(L,N), \+ len(L,N)
will find the extra answer but the program would run forever iflen2/2
was correct. – Tadashilen/2
as a reference implementation. That does not count. We are seeinglen2/2
alone. – Febrifacientlen2(L,N1), len2(L,N2), N1\=N2
. But again this only works because we can generate a faulty input in finite time... – Tadashilen2([1,2,3],X)
produces the run-time errorUndefined procedure: len/2
, not the "two possible answers" semantic "error". – Fraschlen/2
is defined above. And sure you know what a relation that is too general is. – Febrifacientlen2/2
as the defined predicate instead of the intendedlen/2
. Nevertheless, the implementation oflen2/2
uses internallylen/2
. – Febrifacient