How do I create a predicate that takes another predicate and returns a derived version of it?
For example, pairwise predicates can be fairly mechanically extended to apply to lists:
all_whatever(_, []).
all_whatever(X, [Y|T]) :-
whatever(X, Y),
all_whatever(X, T).
What would be the definition of
pairwise_listwise(whatever, all_whatever).
If it's not possible/common/clunky/violates principles, what would be the alternative pattern?
There are two different ways to achieve what you want. The simplest, and likely preferred, way is to define a meta-predicate that takes any binary predicate and applies it to all elements of the list like so:
listwise(_,_,[]).
listwise(P,Y,[X|Xs]) :-
call(P,Y,X),
listwise(P,Y,Xs).
You can then call this as listwise(whatever, Y1, Xs1) to apply whatever to Y1 and each element of Xs1.
This is made possible thanks to the call/N meta-predicate. Note that this meta-predicate can also take partially constructed goals as first argument, so that an alternative formulation could be:
listwise(_,[]).
listwise(P,[X|Xs]) :-
call(P,X),
listwise(P,Xs).
Which is then called as listwise(whatever(Y1),Xs1). This version of the predicate is actually known as maplist/2 instead of listwise, at least in SWI-Prolog (in module library(apply)) and SICStus Prolog (in module library(lists)).
The second way to achieve what you want (actually closer to what you where asking for) is to actually define a new predicate all_whatever/2 using term expansion. Term expansion is a mechanism to rewrite terms when they are loaded (see e.g. for more details in SWI-Prolog: https://www.swi-prolog.org/pldoc/doc_for?object=term_expansion/2). I am showing here the SWI-Prolog version, which is by defining a clause for the term_expansion/2 predicate. This mechanism works differently in different systems or is altogether missing.
term_expansion(pairwise_listwise(PairPred,ListPred), ExpandedTerm) :-
TerminalCall =.. [ListPred,_,[]],
RecursiveCall =.. [ListPred,Y,[X|Xs]],
SingleCall =.. [PairPred,Y,X],
FinalCall =.. [ListPred,Y,Xs],
ExpandedTerm = [TerminalCall, (RecursiveCall :- (SingleCall, FinalCall))].
In this clause, ExpandedTerm is a list defining the two clauses we want to define and all the terms in it are built from the predicate names using =... One can then define the new predicate as follows:
pairwise_listwise(whatever, all_whatever).
When this code is loaded, that clause will be expanded and replaced by two clauses defining the new predicate all_whatever. And now one can call for instance all_whatever(Y1,Xs1).
My preference goes to the first approach (conceptually simpler and works across Prolog versions) but I think it is also useful to be aware of the existence of the term expansion mechanism as well.
Related
The question of the difference between a functor and a predicate in prolog is asked often.
I am trying to develop an informal definition that is suitable for new students.
A functor is the name of a predicate. The word functor is used when
discussing syntax, such as arity, affix type, and relative priority
over other functors. The word predicate is used when discussing
logical and procedural meaning.
This looks "good enough" to me.
Question: Is it good enough, or is it fundamentally flawed?
To be clear, I am aiming to develop a useful intuition, not write legalistic text for an ISO standard!
The definition in https://www.swi-prolog.org/pldoc/man?section=glossary is:
"functor: Combination of name and arity of a compound term. The term foo(a,b,c) is said to be a term belonging to the functor foo/3." This does not help a lot, and certainly doesn't explain the difference from a predicate, which is defined: "Collection of clauses with the same functor (name/arity). If a goal is proved, the system looks for a predicate with the same functor, then uses indexing to select candidate clauses and then tries these clauses one-by-one. See also backtracking.".
One of the things that often confuses students is that foo(a) could be a term, a goal, or a clause head, depending on the context.
One way to think about term versus predicate/goal is to treat call/1 as if it is implemented by an "infinite" number of clauses that look like this:
call(foo(X)) :- foo(X).
call(foo(X,Y)) :- foo(X,Y).
call(bar(X)) :- bar(X).
etc.
This is why you can pass around at term (which is just data) but treat it as a "goal". So, in Prolog there's no need to have a special "closure" or "thunk" or "predicate" data type - everything can be treated as just data and can be executed by use of the call/1 predicate.
(There are also variations on "call", such as call/2, which can be defined as:
call(foo, X) :- foo(X).
call(foo(X), Y) :- foo(X, Y).
etc.)
This can be used to implement "meta-predicates", such as maplist/2, which takes a list and applies a predicate to each element:
?- maplist(writeln, [one,two,three]).
one
two
three
where a naïve implementation of maplist/2 is (the actual implementation is a bit more complicated, for efficiency):
maplist(_Goal, []).
maplist(Goal, [X|Xs]) :-
call(Goal, X),
maplist(Goal, Xs).
The answer by Peter Ludemann is already very good. I want to address the following from your question:
To be clear, I am aiming to develop a useful intuition, not write legalistic text for an ISO standard!
If you want to develop intuition, don't bother writing definitions. Definitions end up being written in legalese or are useless as definitions. This is why we sometimes explain by describing how the machine will behave, this is supposedly well-defined, while any statement written in natural language is by definition ambiguous. It is interpreted by a human brain, and you have no idea what is in this brain when it interprets it. As a defense, you end up using legalese to write definitions in natural language.
You can give examples, which will leave some impression and probably develop intuition.
"The Prolog compound term a(b, c) can be described by the functor a/2. Here, a is the term name, and 2 is its arity".
"The functor foo/3 describes any term with a name foo and three arguments."
"Atomic terms by definition have arity 0: for example atoms or numbers. The atom a belongs to the functor a/0."
"You can define two predicates with the same name, as long as they have a different number of arguments."
There is also the possibility of confusion because some system predicates that allow introspection might take either a functor or the head of the predicate they work on. For example, abolish/1 takes a functor, while retractall/1 takes the predicate head.....
Currently I am studying prolog programming. While I am studying member list (if element is there on list)it show an error when I am consulting.
?-
Warning: c:/users/h.m.thaheed/pictures/snippingtool++/uploads/exx.pl:1:
Singleton variables: [R]
Warning: c:/users/h.m.thaheed/pictures/snippingtool++/uploads/exx.pl:2:
Singleton variables: [Y]
Rule has follows:
member(X,[X|R]).
member(X,[Y|R]) :- member(X,R).
Please help me to solve this.
Those are not errors, but warnings. The Prolog interpreter warns that it can only find one occurrance of one or more variables, which is strange, since typically variables are used to perform unification, pass values from one predicate to another, etc.
Defining a variable a single time makes only sense as a placeholder: when you need to specify a variable to perform correct unification. But Prolog has a standardized way to do that: with an underscore _. You can see an underscore as a "throw away variable". If a predicate contains two (or more) underscores, then those are two (different) variables. The purpose of the underscores is to make it clear that this is a placeholder, and it will thus suppress the warning.
You can thus rewrite the predicate to:
member(X,[X|_]).
member(X,[_|R]) :- member(X,R).
Note that many implementations of member/2 use a member/3 helper predicate to avoid unpacking each "cons" (list element) twice, for example in SWI-Prolog we see:
member(El, [H|T]) :-
member_(T, El, H).
member_(_, El, El).
member_([H|T], El, _) :-
member_(T, El, H).
So here we unpack the list in parameters of the predicate call. This means that the first clause and the second clause of member_ do not need to perform unpacking in the head (we here make some assumptions as to how the interpreter exactly works of course), and in the last clause of member_, we only unpack the next cons once.
The maplist/3 predicate has the following form
maplist(:Goal, ?List1, ?List2)
However the very similar function findall/3 has the form
findall(+Template, :Goal, -Bag)
Not only does it have a goal but a template as well. I've found this template to be quite useful in a number of places and began to wonder why maplist/3 doesn't have one.
Why doesn't maplist/3 have a template argument while findall/3 does? What is the salient difference between these predicates?
Templates as in findall/3, setof/3, and bagof/3 are an attempt to simulate proper quantifications with Prolog's variables. Most of the time (and here in all three cases) they involve explicit copying of those terms within the template.
For maplist/3 such mechanisms are not always necessary since the actual quantification is here about the lists' elements only. Commonly, no further modification happens. Instead of using templates, the first argument of maplist/3 is an incomplete goal that lacks two further arguments.
maplist(Goal_2, Xs, Ys).
If you insist, you can get exactly your template version using library(lambda):
templmaplist(Template1, Template2, Goal_0, Xs, Ys) :-
maplist(\Template1^Template2^Goal_0, Xs, Ys).
(Note that I avoid calling this maplist/5, since this is already defined with another meaning)
In general, I rather avoid making "my own templates" since this leads so easily to misunderstandings (already between me and me): The arguments are not the pure relational arguments one is usually expecting. By using (\)/1 instead, the local variables are somewhat better handled and more visible as being special.
... ah, and there is another good reason to rather avoid templates: They actually force you to always take into account some less-than-truly-pure mechanism as copying. This means that your program may expose some anomalies w.r.t. monotonicity. You really have to look into the very details.
On the other hand without templates, as long as there is no copying involved, even your higher-order predicates will maintain monotonicity like a charm.
Considering your concrete example will make clear why a template is not needed for maplist/3:
In maplist/N and other higher-order predicates, you can use currying to fix a particular argument.
For example, you can write the predicate:
p(Z, X, Y) :-
Z #= X + Y.
And now your example works exactly as expected without the need for a template:
?- maplist(p(1), [1,2,3,4], [0,-1,-2,-3]).
true.
You can use library(lambda) to dynamically reorder arguments, to make this even more flexible.
What is the salient difference between these predicates?
findall/3 (and family, setof/3 and bagof/3) cannot be implemented in pure Prolog (the monotonic subset without side effects), while maplist/N is simply a kind of 'macro', implementing boilerplate list(s) visit.
In maplist/N nothing is assumed about the determinacy of the predicate, since the execution flow is controlled by the list(s) pattern(s). findall/3 it's a list constructor, and it's essential the goal terminate, and (I see) a necessity to indicate what to retain of every succeeded goal invocation.
I am looking for a reliable way to pass an implicit parameter among predicates, e.g.,
p(Context, Args) :- goal(Args).
where goal(Args) should be expanded to something like interpret(Context, goal, Args).
However, since I don't know whether a term will be evaluated as a goal or used as data, I'd like to attach Context to goal as extra data without modifying its actual structure.
Is there any way to do this?
Also, I'd need some way to hook into the evaluation of a term, then fetch its context and call interpret.
Any Ideas?
I am using SWI Prolog, a portable solution would be fine, but is not required.
Edit:
In pseudocode, what I am roughly looking for is the following:
term_expansion((Head :- Body), (Head :- Body)) :-
arg(1, Head, Context),
forall T: CompoundTerm in Body =>
set_term_attribute(T, context, Context).
on_evaluate(T) :-
get_term_attribute(T, context, Context) -> interpret(Context, T) ;
call(T).
Check out the important predicates term_expansion/2 and goal_expansion/2.
They allow you to rewrite clauses at compilation time, and these or similar constructs are supported by all serious Prolog systems to provide an expansion mechanism for Prolog code.
Definite clause grammars (see dcg for more information) are often implemented using such mechanisms. In fact, to pass around a context, DCG notation is itself often already sufficient, so you may not even have to rewrite the code yourself in your use case.
See the DCG resources for more information.
Easy. Use a Logtalk parametric object. You can use this solution with twelve Prolog systems, including SWI-Prolog. For example:
:- object(foo(_Parameter1, _Parameter2)).
:- public(p1/1).
p1(Parameter1) :-
parameter(1, Parameter1).
:- public(p2/1).
p2(Parameter2) :-
parameter(2, Parameter2).
:- end_object.
?- foo(1,2)::p1(P1).
P1 = 1.
?- foo(1,2)::p2(P2).
P2 = 2.
The parameters are logical variables, with all the benefits and potential that entails. The parameters are implicitly shared by all object predicate clauses. In SWI-Prolog, you can even use dicts for parameter values.
It's also easy to provide default parameter values. For example:
:- object(foo, extends(foo(a,b))).
:- end_object.
?- foo::p1(P1).
P1 = a.
?- foo::p2(P2).
P2 = b.
You can find several examples of using parametric objects in the Logtalk distribution.
HI i have a simple knowledge database defined as:
carClass('X1','Oil','small').
carClass('X2','gas','big').
carClass('X3','Petrol','big').
carClass('X4','oil','small').
carClass('X5','Oil','small').
carClass('X6','gas','big').
I am trying to write a rule that will answer the query: Display all carClass that runs on 'oil' and IS NOT 'big'.
I am trying to implement it using:
OnOilButNotBig :-
carClass(CarClass,'oil',_),
carClass(CarClass,'oil', \+('big') ),
write(CarClass).
but this is not working.
You have to understand the difference between a predicate and a functor.
If we oversimplify things a bit, a predicate is an identifier at the top level, so carClass/3 is a predicate, write/1 is a predicate and onOilButNotBig/0 is. You can call a predicate. A predicate with filled in arguments is a goal.
A functor on the other hand is an identifier not on the top level. Constants are functors, variables are functors, and functions with arguments are functors. Examples of functors are 'X1', 'oil' and foo(X,bar,qux(2)).
The negation expects a goal. 'big' in this case is not a goal, in fact \+('big') itself is a functor.
You can only solve this by turning the condition into a goal and ensure you will call it. This can be done like:
onOilButNotBig :-
carClass(CarClass,'oil',_),
carClass(CarClass,'oil',X),
\+(X = 'big'),
write(CarClass).
Furthermore I do not really see why you call carClass/3 twice. An equivalent and slightly more efficient program is the following:
onOilButNotBig :-
carClass(CarClass,'oil',X),
\+(X = 'big'),
write(CarClass).
Finally as #Repeat noted, you need to use names that start with a lowercase for predicates and functions.
First things first!
The code doesn't compile1.
Why? Predicate names usually start with lowercase characters2.
My advice: instead of OnOilButNotBig write onOilButNotBig!
To express term inequality, use the right prolog-dif goal(s), like so:
onOilButNotBig :-
dif(X, big),
carClass(CarClass, oil, _),
carClass(CarClass, oil, X),
write(CarClass).
As a side remark, there are a few more issues with your code:
Use side-effect based I/O only when necessary.
In most cases, it is preferable to use the interactive prolog-toplevel for data input/output!
onOilButNotBig(CarClass) :-
dif(X, big),
carClass(CarClass, oil, _),
carClass(CarClass, oil, X).
For the sake of readability, please do not use atoms like 'oil' and 'Oil'.
Pick one and stick to it! I suggest oil (lowercase) which does not need escaping.
The goal carClass(CarClass, oil, _) is completely redundant.
Why? It is a generalisation of the close-by goal carClass(CarClass,oil,X).
Footnote 1: When using b-prolog 8.1, sicstus-prolog 4.3.2, swi-prolog 7.3.14, and xsb 3.6.
Footnote 2: Names can also starting with uppercase characters if the right (escaping with single-quotes) is utilized.
Footnote 3: In general, redundant goals are ok, but they suggest to me your code will likely not behave as intended.