Why is findall/3
in place of bagof/3
or setof/3
a subtle bug? Do you mean because it doesn’t sort the list of results, like setof/3
?
It’s not always a bug, but it can be. The main advantage of findall/3 over bagof/3 is that you don’t need to add the “existential” markers.
Anyway, here’s a long discussion of the problems with findall/3, gathered from various sources: https://github.com/dtonhofer/prolog_notes/blob/45f15e13a093c7ea8da5f90f6af662592f7bd891/swipl_notes/about_findall/README.md
Thanks, I was curious if I could find something on the internet and, searching, I found dtonhofer’s github repo that you link to above. I gave it a quick once-over. It’s a bit long and I’m too tired, I’ll have to make some time to read it more carefully tomorrow.
I would say that findall/3
is a bug as often as anything else in Prolog is a bug. findall/3
can be dangerous in the hands of students and I’ve sure been surprised by its behaviour in the past but eh, every few years Prolog does something to remind me it doesn’t love me like I do, anyway.
I like how O’Keefe puts it in dtonhofer’s repo. findall/3
is for when you need iteration in Prolog. Well, sometimes you do. As sometimes you need “assignment” (cough setarg/3
cough).
For iteration, I use forall/2 (and sometimes maplist).
library(aggregate) provides both logical versions (bagof, setof) and the non-logical version (findall).
My tl;dr for findall/3 is that it can be used to define non-logical predicates not/1 and var/1 – I think it was Lee Naish who first mentioned this:
another_not(Goal) :- findall(., Goal, []).
another_var(X) :- findall(_, (X=a; X=b), [_,_]).
My code often contains setof(X, pred(X), Xs) -> true ; Xs = [])
.
Is that better or worse than findall(X, pred(X), X0s), sort(X0s, Xs)
? – I leave that to the philosophers debating the number of angels dancing on the head of a pin.
No, the main advantage of findall is it’s fast. I never use the other two unless I have a complex case that can only be handled with those existentials. I did use setof and bagof at one time, until I started performance profiling my code and realized bagof dominated the time cost, and replacing with findall solved this problem.
Further, I disagree that any of the problems indicated in the notes are notable in practice.
It’s necessarily a limitation of an answer-collecting predicate that it’s usually only sensible if it terms collected are ground. (This is overstated, but in practice it’s a reliable guide. If you don’t try to do fancy things, you won’t get bit.)
setof is not a first-order logical predicate. It’s also not standard logic despite attempts. If I ask “what is the set of all X such that foo(X)”, I (thinking logically) expect a set, whether empty or not. I don’t expect finding no matches to mean I need to abandon my current train of thought. And in practice, I virtually never found that fail
is a more useful answer than X = []
.