Programming cooperation

For the basic predicates listed on Finding all Solutions to a Goal, i.e. findall/3,bagof/3 and setof/3 which follow the form of

functor(+Template, :Goal, -List)

here is what I consider and think of when using them.

If one has the need to convert facts or results from predicates into a list for further processing then these are most likely the predicates that will be used. More advanced predicates fulfilling the same need are in

library(aggregate): Aggregation operators on backtrackable predicates

but it is easier to start with the first three before learning the aggregate predicates.

If you already have a list then

library(apply): Apply predicates on a list

is probably what you need.

In understanding the predicates one needs to understand the instantiation patterns of the predicate. (ref)

pattern Description
+ Argument is fully instantiated at call-time, to a term that satisfies the type. This is not necessarily ground, e.g., the term [_] is a list, although its only member is unbound.
: Argument is a meta-argument. Implies +.
- Argument is an output argument. It may be unbound at call-time, or it may be bound to a term. In the latter case, the predicate behaves as if the argument was unbound, and then unified with that term after the goal succeeds. For example, the goal findall(X, Goal, [T]) is good style and equivalent to findall(X, Goal, Xs), Xs = [T] 3 Determinism declarations assume that the argument is a free variable at call-time. For the case where the argument is bound or involved in constraints, det effectively becomes semidet, and multi effectively becomes nondet.

That is a bit to take in at first so will explain it more for the three predicates as each is covered.

Normally arguments follow the pattern of input, then output so that when using the arguments one would think about them simply from left to right.

Here that is not the case as the argument that I consider the input argument to be the second argument, Goal which is a predication. Think of this as the name of the fact, predicate or Goal you would query from the top level to repetitively show the values or results of the Goal. The pattern : just informs me that the argument may/can include the module name, e.g. <module>:<predication>.

The next argument I consider is the first one, Template, and think of it as what the result should look like. For example if the Goal is person(Name,Address,Phone) and only the Name values are needed then the template would be Name. If more then on one value is needed to be collected then I usually put them in a compound term with a functor, e.g. item(Name,Address).

When first learning these three predicates the point which was not obvious to me but crucial to understanding the predicates and until understood will make these predicates very frustrating to use is noted in the documentation as

findall/3 is equivalent to bagof/3 with all free variables appearing in Goal scoped to the Goal with an existential (caret) operator (^ ), except that bagof/3 fails when Goal has no solutions.

I have even asked questions about the existential operator over the years in trying to understand it.

Basically this is how I think about it while I type in code.

1. Do I need unique and sorted values?
    a. Yes - use setof/3
    b.  No 
         2. The call should always succeed even if no results? or
            Is an empty list as the result allowed?
             a. Yes - use findall/3 which will not fail and return empty list if no solutions.
             b. No - use bagof/3 which will fail if no solutions.

Note: If you need sorted with duplicates then setof/3 can not be used as it will remove duplicates when sorting.

With that out of the way then if findall/3 is used there is no need to think about the existential operator. For the other two the existential operator is needed otherwise one can get multiple results on backtracking which is hard to debug. (ref)

Note: When noting the existential operator (^)/2 I strive to use the words existential operator as most search engines can not find anything using just ^/2. The only one that gets a hit is the SWI-Prolog documentation search, i.e. ^/2

The next thing I have learned over the years about the existential operator is that depending upon ones background the understanding about it they bring when asking about it has a lot to do with understanding how they discuss it. Those with a math background and use to proofs talk one way, while those with a logic background seem to use another and those who are talking about it for the first time when asking a question seem to be totally confused, which was me for a while.

So as I code the Goal argument as needed if the existential operator is needed I do this.

  1. Write the Goal with the functor and all of the arguments as unique named variables, no singleton variables, e.g. _A. Do not add the existential operator just yet.
  2. Write the Template using the names of the variables that are to be collected and leaving out the names of the variables that are not needed.
  3. Finish the Goal.
    a. Add ^ before Goal, thinking of ^ as a binary operator used as an infix.
    b. Add [] before ^ which is the one of the two arguments to the ^ binary operator and can be a list. For single values it does not have to be in a list, e.g. A^person(Name,A).
    c. Account for all of the variables in Goal, if they are not used in the Template then add them to the list for the existential operator.

Note: I tend to change the names of the variables used with the existential operator from the user friendly names to an alphabetical list, e.g. [A,B,C,D] as it makes it easier to identify the arguments to be collected and the arguments to be discarded but also easier to ensure all of the variables are accounted, e.g. bagof(item(Name,Location),[A,B]^person(Name,A,Location,B),Items)

The last argument is List which is just a name for the list. If template is just a single variable then I just use the plural of the name, e.g. for a Template of Name the List would be Names. If Template is a compound, then I often use a functor of item and for Name use Items.

I know that is a lot of details and seems like a lot to take in when typing code but once you learn these three you find that you are using them so often you don’t know why you didn’t learn to use them sooner.

HTH

1 Like