The following looks very unusual :
?- findall(X, member(X, [1, 2, 3]), X).
X = [1, 2, 3].
The trace even more so
?- trace, findall(X, member(X, [1, 2, 3]), X).
^ Call: (11) findall(_100058, member(_100058, [1, 2, 3]), _100058) ? creep
^ Exit: (11) findall([1, 2, 3], user:member([1, 2, 3], [1, 2, 3]), [1, 2, 3]) ? creep
X = [1, 2, 3]
Thinking in terms of semantics of findall this makes little sense. What is going on?
To expand on my comments, maybe this might help:
If you look closely, you will see that Prolog (SWI, in this case) did not print a substitution for
X. This means thatXis not bound when the query succeeds. Indeed:This does not mean that
Xis never bound while the query executes:But after all solutions have been generated,
Xis unbound and can be bound to some other value -- such as the list of solutions. This will work in any standard conforming Prolog, as the standard says explicitly thatfindallonly tries to unify its third argument after it has created the list of solutions. It even contains an example with sharing between the template and the list of instantiations:So how does this binding and unbinding work? With a failure-driven loop, as quoted in rajashekar's answer from the SWI-Prolog implementation. In general, succeeding predicates bind some variables. When at some later point something fails (or, equivalently, the user presses ; when prompted by the toplevel), backtracking takes place: It unbinds variables to allow them to take new values, then retries some goal.
What goes on inside
findallis the same as goes on when you write the following:So while
findallis very impure, it is not so impure as to be completely un-Prolog-like. In fact, we can write our own:This behaves as expected:
Or even:
A minor problem with this is that the instantiation of
Goalshould be checked. A major problem with this is that allmy_findallcalls share the same bag, so callingmy_findallfrom inside amy_findall(or in parallel) will make you unhappy. This could be fixed using some sort ofgensymmechanism to give eachmy_findallrun its unique key into the database.As for the trace output, it is an unfortunate consequence of wanting to express "your goal succeeded with such-and-such bindings" on one line. At the point of success, it is true that
findall(X, ..., X)succeeded, and it is true thatX = [1, 2, 3], and hence it is true that the successful instance of the goal isfindall([1, 2, 3], ..., [1, 2, 3]).Consider:
For example:
So
forty_two(42)is a succeeding instance offorty_two(X). Even thoughforty_two(42)does not succeed:It is logical that printing the term
forty_two(X)in an environment withX = 42printsforty_two(42). I think the problem is that this logical behavior sticks out as strange among all the non-logical stuff going on here.