I already program for more than 20 years in solitude, and I program in prolog for the same period, and i know that even in prolog there is a lot for me to learn about things which i have never used before.
For example the bagof operator i still dont understand at all, regular expressions are completely unlogical to me, and the Arrow operator in prolog i dont understand.
Does the Arrow operator in Prolog, Typescript an PHP have any similarities? in PHP it is an object notation, in Typescript it stands for about anything.
i would like to gather for example for 1 time every 6 months ( 2 times a year ) somewhere in amsterdam, ( with mouth -protection ( in dutch: mondkapje ) ) .
in my life until now i have never been in the situation where i could cooperate with other Technical people.
I have been in the proximity of other programmers, but this was allways in Competitive ( commercial ) context, where programmers work Against eachother, instead of cooperate with eachother.
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
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.
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.
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.
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.
this basic step-plan for the use of findall , bagof and setof is perfect, gives me a lot more understanding also. up until now i have allways chosen to use findall because the other 2 were completely unclear to me. When i use findall, then after the findall i would adjust a list processing or/and any sorting for the specific task.
can i ask this question: can it be said that in the case of the name : Bagof / bagof the name of the predicate could have also or would have instead better have been : findall_pairs ? because the meaning of Bag in Bagof is zero?
Personally I ask myself that question often for more than just those predicates, e.g.
For this use of concurrent_forall instead of concurrent_forall(:Generate, :Test) I think of it as concurrent_forall(:Generate unique threads values, :Call thread with unique values).
For such cases where it was a revelation to think of it differently I will often add a comment in my code or note it when writing about it. In some cases I will even add a wrapper predicate with the new name so that the reading of other predicates using it is more natural. Yes I know that will slow down the code but if code can not be understood then it is likely to be tossed by those maintaining it.
Personally I would not call them predicates, I would refer to them as facts.
While Prolog does have an official standard and the standard does include terminology, as the standard is copyright it can not be handed out freely for those of us that have a copy. However in answering certain questions such as you ask, this StackOverflow answer includes the related terminology.
The definition I go with for Prolog fact most of the time is that it is a clause with only a head and the parameters are all ground.
That is what I would expect.
My current favorite technique for understanding code methods/functions/predicates is to download Git repositories that contain code I trust to be done correctly. All of the repositories are kept in a separate directory with a name like SWI-Prolog (Reference code for searching) and then using a command line tool such as grep or an IDE with a good search engine and visual summary such as NotePad++ I search for the method/function/predicate name and then look at the results.
Here is a list of Git Repositories containing Prolog code that you can trust will be done correctly.
My current set in `SWI-Prolog (Reference code for searching)` (Click triangle to expand)
Neither do I really since -> is identical to , !, making it pure language clutter.
Iām guessing the history of -> is since programming languages invariably have something along the lines of IF some_test(A, B) THEN do_this(A, B) ELSE do_that(A,B), so it became a prolog convention to imitate this with
( some_test(A, B)
-> do_this(A, B)
; do_that(A, B)
),
In my opinion, a clearer way to write that is
my_rule(A, B) :-
some_test(A, B), !,
do_this(A, B).
my_rule(A, B) :-
do_that(A, B).
Reasons I donāt like the -> syntactic sugar for , !, is itās confusingly similar to DCGās --> operator and classical logicās implies operator ā (which confuses nearly everyone, Iāve put some notes on my interpretation here).
Synonyms are a pet hate of mine because whenever you have more than one way to say exactly the same thing, some pedant will come along and invent rules on why one way is better than another, as Iāve done above.
I donāt think so. Some Prolog purists consider cut harmful because it limits your query to only one result, which can be dangerous when doing database-type stuff. Prolog-inspired SQL-alternative Datalog specifically excludes the ! operator to avoid losing rows which might otherwise match.
A newby mistake I made, and I suspect is quite common, was writing extremely convoluted if ... then ... else if ... code which got completely unreadable when I started nesting if .. then ... within outer then/else blocks. Iāve found writing a separate stanza for each or branch made my code way easier to maintain and debug.
A rule of thumb I have is whenever I find myself using the ; operator, I ask myself I this should be split into a separate stanza of a rule. A big selling point of Prolog is you can do that easily, whereas in other programing languages you end up with lots of cases in a switch block or worse yet nested if.. then ... else... spaghetti.
No, (->)/2 and !/0 are very different, e.g. there is no āelseā in a cut, and (->)/2 does not prevent backtracking into another predicate with the same name & arity.