Sterile choicepoint left open in SWI-Prolog 8.3.5 vs in 8.3.4

Update: It’s the foreach/2 actually. If I replace it with forall/2, there are no open choicepoints left in 8.3.5 and 8.3.4. I have to run some more tests I guess.


After compiling 8.3.5 I ran my library test cases and was surprised to see a test emitting a warning that “Test succeeded with choicepoint”.

I have poked around the test case and found that for some reason a call to append/3 is not well-behaved (that’s the technical term as I understand it); it leaves a useless choicepoint in one 8.3.5, but none in 8.3.4. Running the append on the toplevel however gives a deterministic answer in both versions.

The source of the nondeterminism is ghostly though. I try to simplify things and it disappears or appears. :sweat:

Is wandering non-determinism expected (maybe due to compiler optimizations?) Should I continue giving myself a headache?

The code, pared down:

:- begin_tests(what).

test(list_and_suffix_known) :-
   List = [a],
   length(List,ListLen),!,
   succ(SuffixLen_max,ListLen),!,
   foreach(between(0,SuffixLen_max,SuffixLen),list_and_suffix_known(List,SuffixLen)).

list_and_suffix_known(List,SuffixLen) :-
   length(Suffix,SuffixLen),!,

   debug(splinter_test),
   debug(splinter_test,"List=~q Suffix=~q",[List,Suffix]),
   nodebug(splinter_test),
   append([_,Suffix],List),
   ! % A cut here makes things deterministic in 8.3.5, whereas they already are in 8.3.4
   .

:- end_tests(what).

Bizzardedly, this pre-digested code has open choicepoints in both version. Am I going crazy?

:- begin_tests(what).
  
test(list_and_suffix_known) :-
   /*
   List = [a],
   length(List,ListLen),!,
   succ(SuffixLen_max,ListLen),!,
   foreach(between(0,SuffixLen_max,SuffixLen),list_and_suffix_known(List,SuffixLen)).
   */
   list_and_suffix_known([a],0).

list_and_suffix_known(List,SuffixLen) :-
   length(Suffix,SuffixLen),!,

   debug(splinter_test),
   debug(splinter_test,"List=~q Suffix=~q",[List,Suffix]),
   nodebug(splinter_test),
   append([_,Suffix],List)
   .

:- end_tests(what).

Why not append(_, Suffix, List)? That is what append/2 calls in the end anyway, but after a type check and some intermediate steps. That also explains your problem: append/3 is only semidet if the first argument is a (proper) list.

1 Like