Implementing user-defined arithmetic functions - prolog

How can I add a function (e.g., hammingweight) and use it in expressions occuring in the right-hand side is some (is)/2 goal?
Could something like goal_expansion or term_expansion help here?
I acknowledge that this is not a big feature, but it could increase readability of some of my Prolog programs.
Writing a custom (is)/2 predicate (implementing a custom expression evaluator) is do-able, but I would like to keep runtime overhead low, as I don't want to sacrifice readability for runtime overhead in this case.

There is no such provision in ISO Prolog, neither to extend (is)/2 nor to rely on goal expansion. And, looking at the various implementation specific features that are offered by some implementations to this end, there is no generally agreed upon way to do this. So implementing your own (my_is)/2 seems to be the best way to go.
Also note, that this would not only affect (is)/2 but also all other built-ins that use evaluable functors. In particular, all arithmetic comparison built-ins (8.7 Arithmetic comparison) (see this overview) would be affected.

My simple minded (~20 LOC) syntax sugar, lifter, it's based on goal_expansion.
With lifter, the clause
longer(A,B) :-
length(A,º) > length(B,º).
is expanded to
longer(A, B) :-
length(A, C),
length(B, D),
C > D.

You can use Logtalk's term-expansion mechanism, which is portable and works with its twelve supported Prolog compilers (*). Logtalk compiling and loading and loading predicates accept Prolog files and will output the corresponding Prolog expanded files. For example, assuming that the file you want to expand is named source.pl and that your term_expansion/2 and goal_expansion/2 predicate definitions are in a file named expansions.pl, you can do something like:
% first, load the expansion hooks:
| ?- [expansions].
...
% second, expand a file using those hooks:
| ?- logtalk_compile(source, [hook(user)]).
...
You will get the expanded file, which will be (by default) named source_pl.pl (in a directory that will depend on the value of the scratch_directory Logtalk flag). If the expansions are contained in a Prolog module, use above the module name instead of user. If the source.pl file contains a module instead of plain Prolog code, you'll need to define some clauses for the term_expansion/2 predicate to avoid Logtalk compiling the module as an object. But in the simpler case where you're not using modules, the two queries above should suffice.
One feature of Logtalk's term-expansion mechanism that might be useful is that you can mark a term or a goal to not be expanded or further expanded by wrapping it with the {}/1 control construct.
(*) Note that term-expansion mechanism are not standard, not provided by all Prolog implementations, and with significant differences between implementations.

Many Prolog systems don't allow user defined evaluable predicates.
Sometimes the problem is that the evaluable predicates live in
another namespace. Sometimes the Prolog system shyes away from jumping back into Prolog execution while doing an arithmetic evaluation in the host language.
Some Prolog systems have nevertheless user defined evaluable predicates, like ECLiPSe Prolog and Jekejeke Prolog. In Jekejeke Prolog the overhead was two fold. To my surprise in the new Dogelog runtime user definable evaluable
predicates show an advantage:
/* Dogelog Runtime 0.9.5 */
fact(0, 1) :- !.
fact(N, X) :- M is N-1, fact(M, Y), X is Y*N.
fact2(0, 1) :- !.
fact2(N, X) :- X is fact2(N-1)*N.
for(_).
for(N) :- N > 1, M is N - 1, for(M).
% ?- time((for(1000), fact(1000, _), fail; true)).
% % Wall 1595 ms, trim 0 ms
% ?- time((for(1000), _ is fact2(1000), fail; true)).
% % Wall 1394 ms, trim 0 ms
I am attributing the slight speed advantage to the more concise formulation in fact2/2. The formulation uses less Prolog logical variables. And the user defined evaluable predicates capable (is)/2 internally does also no Prolog logical variables, the host language assigments that happen do not allocate
a Prolog logical variable or trail the same, so that in the end there is a speed-up.

Related

functor vs predicate - definition for students

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.....

Guidelines for implementing predicates like dif/2

Suppose I have a predicate foo/2 which defines a relation between its first and second argument.
What is the most idiomatic and efficient way to change the implementation of foo/2 such that:
if both of its arguments are ground, it acts as before (succeeds if the relation holds, fails otherwise).
if one of the two arguments (or both) are free, it "constrains" those two arguments so that when they will get grounded, the relation will be checked.
In other words, how to correctly implement the behaviour exhibited by dif/2 but with any kind of user-defined relation?
listing(dif/2). was of little help.
Different Prolog implementations provide different features to accomplish this. The mechanism is variously known as coroutining, delayed goals, constraints, and your Prolog system's manual will provide more information.
Here are two variants, which are available in SICStus Prolog and also some other systems.
block/1 directive
In SICStus Prolog (and possibly some other systems), one way to lift a user-defined predicate to such a constrained version is available via the declarative block declaration.
Interestingly, this does not require any changes to the predicate itself!
Suppose you have an impure version of dif/2, using the non-monotonic (\=)/2 predicate:
madif(X, Y) :-
X \= Y.
Then you can turn it into a delayed version for example with:
:- block madif(-, ?),
madif(?, -).
madif(X, Y) :-
X \= Y.
Sample queries and answers:
| ?- madif(a, b).
yes
| ?- madif(a, X).
user:madif(a,X) ? ;
no
| ?- madif(a, X), X = b.
X = b ? ;
no
| ?- madif(X, Y).
user:madif(X,Y) ? ;
no
As required, the evaluation of the goal is delayed until both arguments are instantiated.
when/2
A second way to accomplish this with SICStus Prolog (and other systems that provide this feature) is to use when/2. This requires changes to the predicate itself.
For example, using when/2, you can implement madif/2 like this:
madif(X, Y) :-
when((ground(X),
ground(Y)), X \= Y).
Sample queries and answers:
| ?- madif(X, a).
prolog:trig_ground(X,[],[X],_A,_A),
prolog:when(_A,(ground(X),ground(a)),user:(X\=a)) ? ;
no
| ?- madif(X, a), X = b.
X = b ? ;
no
First and foremostly,
Take the user's viewpoint
... and not that of an implementer. All too often this is ignored – also in existing constraint implementations. And it shows. So here are the most salient aspects to take into account.
Correctness
Obviously this should hold. It is always better to produce clean errors, mostly instantiation errors, better to flounder forever, even better to loop forever than to fail incorrectly. If all else breaks you can wrap your attempt with freeze(_, G_0). Note that you do need a working toplevel to actually see such floundering goals. SICStus has such a toplevel1, in SWI you need to wrap your query as call_residue_vars(Query_0, Vs) to see all attached constraints.
Consistency
Next you want to ensure that your constraint ensures consistency as much as possible. There are many notions of consistency like, domain and bounds consistency. To take your precise requirement think of difgrn/2 and compare it to the built-in dif/2:
difgrn(X, Y) :-
when((ground(X), ground(Y)), X \== Y).
| ?- difgrn(X, X).
prolog:trig_ground(X,[],[X],_A,_B),
prolog:trig_ground(X,[],[X],_A,_C),
prolog:trig_and(_C,[],_A,_B,_A),
prolog:when(_A,(ground(X),ground(X)),user:(X\==X)) ? ;
no
| ?- dif(X, X).
no
| ?- difgrn([], [_]).
prolog:trig_ground(_A,[],[_A],_B,_C),
prolog:trig_and(_C,[],_B,1,_B),
prolog:when(_B,(ground([]),ground([_A])),user:([]\==[_A]))
| ?- dif([], [_]).
yes
One way to implement dif/2 in full strength is to use the very special condition (?=)/2:
difwh(X,Y) :- when(?=(X,Y), X\==Y).
which should answer your question as best as one can:
In other words, how to correctly implement the behaviour exhibited by dif/2 but with any kind of user-defined relation?
But unfortunately, this does not extend to anything else.
The situation becomes even more complex if one considers consistency between various constraints. Think of X in 1..2, dif(X, 1), dif(X, 2).
Answer projections
(For lack of a better word.) Sometimes you want to see your constraints nicely on the toplevel - and the best way is to represent them as goals that themselves will reestablish the exact state required to represent an answer.
See above trig_ground answers, which certainly could be beautified a bit.
Variable projections
Same as answer projections but possible at any point in time, via frozen/2 or copy_term/3.
Subsumption checking
This is useful for diagnostic purposes and loop checks.
For purely syntactic terms, there is subsumes_term/2 which ignores constraints. A prerequisite to perform an effective test is to connect each involved variable to the actual constraint. Consider the goal freeze(X, Y = a) and imagine some subsumption checking with Y as an argument. If Y is no longer attached to the information (as it usually is with current implementations of freeze/2) you will come to the wrong conclusion that this Y subsumes b.
Note as for the actual example of dif/2, this was the very first constraint ever (1972, Prolog 0). A more elaborate description gives Michel van Caneghem in L'anatomie de Prolog, InterÉditions 1986 and Lee Naish in Papers about MU-Prolog.
1 Half-true. For library(clpfd) you need assert(clpfd:full_answer).

Prolog: Passing Implicit Parameter / Predicated Evaluation

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.

Does Prolog always reevaluate rules?

% expensiveComp(+A,-Result)
% otherRule(+Arg1,Arg2+,-Result)
% r(+A,+B,C)
r(A,B,C) :-
expensiveComp(A,Result),
otherRule(Result,B,C).
If r is called multiple times with the same value for A will expensiveComp necessarily be reevaluated each time r is called, or are there circumstances under which Prolog would just bind a cached value to Result?
Are some implementations of Prolog better at knowing when they don't need to reevaluate a rule?
There's a number of Prolog implementations which feature support for different kinds of memoization, commonly called "tabling" in Prolog-lingo: b-prolog, yap, xsb.

Will using member within a forall clause in SWI-Prolog always output the elements in the same order?

Having recently got into Prolog I've been using it for a few simple tasks and began to wonder about using member within forall loops like the one in the trivial example below:
forall(member(A,[1,2,3,4]), print(A)).
In the case that you do something like this is it always true that forall will process the elements within the list in the same order every time its called? Does it have to be enforced by say doing something like:
A = [1,2,3,4], sort(A, B), forall(member(C,B), print(C)).
From what little research I've initially done I'm guessing that it comes down to the behaviour of member/2 but the documentation for the function on SWI-Prolog's website is very brief. It does however mention determinism with regards member/2 which gave me an inkling I might be on the right path in saying that it would always extract the elements in the same order, though I'm far from certain.
Can anyone give me any guarantees or explanations on this one?
Non-determinism in Prolog simply refers to a predicate having potentially more than one solution. Clearly, member/2 is such a predicate. This does not mean that you have to be worried about your computation becoming unpredictable. Prolog has a well-defined computation rule which essentially says that alternative solutions are explored in a depth-first, left-to-right manner. Thus your goal member(X,[1,2,3,4]) will generate solutions to X in the expected order 1,2,3,4.
Sorting the list [1,2,3,4] will not make any difference, as it is already sorted (according to Prolog's standard term order).
A word of caution about forall/2: some Prologs define this, but it is probably less useful than you imagine, because it is not really a "loop". You can use it in your example because you only perform a print side effect in each iteration. For most other purposes, you should familiarize yourself with recursive patterns like
print_list([]).
print_list([X|Xs]) :- print(X), print_list(Xs).
Strictly speaking, there is no guarantee in SWI on several levels:
1mo, that member/2 or forall/2 will perform in exactly this manner, since you can redefine them.
?- [user].
member(X,X).
|: % user://1 compiled 0.00 sec, 2 clauses
true.
?- forall(member(A,[1,2,3,4]), print(A)).
[1,2,3,4]
true.
However, member/2 is defined in the Prolog prologue which covers all the details you are interested in.
As for forall(A,B) it is safer to write \+ (A, \+B) instead, since this relies on standard features only. There is no definition of forall/2 as such, so it is difficult to tell what is the "right" behavior.
2do, that SWI will be standard conforming. If you read the documentation, you will note that there is no self-declaration (as for, e.g. SICStus Prolog) for standard conformance. In fact, \+ (A, \+B) is not fully conforming, as in the following example that should silently fail, but rather prints nonconforming
?- \+ ( C= !, \+ (C,fail;writeq(nonconforming))).
N208 has forall/2 defined + (call(Generator), + call(Test)), so this makes it less dubious. But by virtue that the ISO core standard (+)/1 does already a call/1 and that the ISO core standard (,)/2 will be subject to body conversion one can simply define it as follows in an ISO core standard Prolog:
forall(Generator, Test) :-
\+ (Generator, \+ Test).
SWI-Prolog has also implemented this way, and the error observed by Ulrich Neumerkel will not be seen when using forall/2:
Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.18)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
?- \+ ( C= !, \+ (C,fail;writeq(nonconforming))).
nonconforming
true.
?- forall(C=!, (C,fail;writeq(nonconforming))).
false.
Side remark:
I don't know how useful it is for loop. It seems to me using it for loops is not the right approach, since the test might fail, and then the construct also fails. I have also seen by Striegnitz and Blackburn the following definition of a helper predicate that they call failiure driven loop.
doall(Goal) :-
Goal, fail.
doall(_).
I find myself directly writing Goal, fail; true which also does the job:
Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.18)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
?- member(A,[1,2,3,4]), write(A), nl, fail; true.
1
2
3
4
true.

Resources