If you look at a specific example, you will soon be buried in a multitude of non-local details. So much so that you lose sight of the important properties of your program. Let's look at the next piece of the program called the slice of failure . You get a failure fragment by adding false to your program. Failures share many interesting properties with the original program. For example, a Goal, false target Goal, false , executed with a failover, will never use more outputs than the original program. Or, conversely, the original program will use more (or, at best, the same number) outputs. So let me point out one such snippet:
list_to_set ([], []): - false .
list_to_set ([A | X], [A | Y]): -
list_to_set (X, Y), false ,
\ + member (A, Y) .
list_to_set ([A | X], Y): -
list_to_set (X, Y), false ,
member (A, Y) .
And since this fragment is no longer interested in specific elements ( A no longer used, but member/2 ), we can use length/2 for most common lists. Thus, we can observe the minimum number of conclusions required for each length:
? - length (L, N), call_inferences ((list_to_set (L, _), false; true), Inf).
N = 0, Inf = 3;
N = 1, Inf = 6;
N = 2, Inf = 12;
N = 3, Inf = 24;
N = 4, Inf = 48;
N = 5, Inf = 96;
N = 6, Inf = 192;
N = 7, Inf = 384;
N = 8, Inf = 768;
N = 9, Inf = 1536;
N = 10, Inf = 3072 ...
Using
:- meta_predicate user:call_inferences(0,-). call_inferences( Goal, Inferences) :- statistics(inferences, Inferences0), Goal, statistics(inferences, Inferences1), Inferences is Inferences1 - Inferences0.
The number of pins doubles with each subsequent element. That is, they grow exponentially. Thus, your implementation is worth at least an exponentially many conclusions ... No need to look at a specific example.
Your program has more problems:
?- L=[A,B],list_to_set(L,S), L = [a,b].
doesn't work whereas
?- L=[A,B], L = [a,b], list_to_set(L,S).
succeeds. That is, your program is no longer a pure relation. Use maplist(dif(A),Y) instead of \+ member(A,Y) .