Strange behavior with findall/3 (original) (raw)
Hello, I am using SWI version 9.2.2 (on macOS), and have a concern related to findall/3
.
According to the documentation:
findall(+Template, :Goal, -Bag):
Create a list of the instantiations Template gets successively on backtracking over Goal and unify the result with Bag. Succeeds with an empty list if Goal has no solutions.findall/3
is equivalent tobagof/3
with all free variables appearing in Goal scoped to the Goalwith an existential (caret) operator (^
), except thatbagof/3
fails when Goal has no solutions.
Below are highly simplified versions of two versions of an extract from my code, but they do not produce the same answer:
findall(I, between(1, 2, I) -> true, Is).
% Is = [1]
findall(I, between(1, 2, I), Is).
% Is = [1, 2]
Is this intentional? Is it due to the way in which findall/3
is implemented?
Thank you in advance.
Boris May 2, 2024, 7:55pm 2
Try just the goals, without findall, and you will see the same:
?- between(1, 2, X) -> true.
X = 1.
?- between(1, 2, X).
X = 1 ;
X = 2.
So this is not about findall, maybe it is the if-then ->/2.
brebs May 2, 2024, 8:13pm 3
The meaning of ->/2
is crucial - why use it here? An alternative: *->/2
, purely for comparison:
?- findall(I, between(1, 2, I) *-> true, Is).
Is = [1, 2].
I had accidentally used ->/2
here, and this is what caused me to discover this behavior. The following code has the same behavior as the ->/2
case:
findall(I, (between(1, 2, I), !), Is).
I’m wondering why the cut causes findall/3
to behave like findnsols/4
when cutting behavior proceeds the first valid solution, despite variables in Goal
being supposedly bound to each instance of Goal
.
I think this makes sense now — the cutting behavior prevents backtracking on I
which causes this behavior. Thank you for your answer and apologies for any inconvenience caused.