Prolog systems aiming at iso-prolog conformity do not have to support compound terms with arbitrarily large arities. The Prolog flag max_arity reflects this.
According to ISO/IEC 13211-1:1995:
7.11.2.3 Flag: max_arity
Possible values: The default value only
Default value: implementation defined
Changeable: No
Description: The maximum arity allowed for any compound term, or unbounded when the processor has no limit for the number of arguments for a compound term.
So Prolog systems may or may not impose an upper limit on max_arity ... but what about the lower limit? Is, say, 5 okay?
NO!
ISO/IEC 13211-1:1995/Cor.2:2012, Technical Corrigendum 2, defines call/2..8 as:
8.15.4 call/2..8 ...
NOTE — A standard-conforming processor may implement call/N in one of the following ways because error condition d is implementation dependent (3.91).
Implement only the seven built-in predicates call/2 up to call/8.
Implement call/2..N up to any N that is within 8..max_arity (7.11.2.3). Produce existence errors for larger arities below max_arity.
Implement call/9 and above only for certain execution modes.
All of these ways only imply Max_arity >= 8—but nothing more.
So my question has (at least) two sides:
Prolog User:
"What is the maximum arity I may use if I want to avoid vendor lock-in?"
Prolog Implementor:
"What is the smallest Max_arity I must support if I aim1 at ISO-Prolog conformity?"
Right now, I'm quite sure the answer is this:
Yes, Max_arity = 8 is okay.
But is it in fact so? Are there clues I am missing?
Footnotes: 1) I do.
Ad 2: Standard conformity is a precondition for a working system. It is by no means a guarantee that a system is fit for any purpose including the one you are (reasonably) interested in. Given that, I'd say 1 is the minimum, as zero cannot be the arity of a compound term. Of course, any attempt to produce a list would produce a representation error. It could be worse, see Purpose of this.
The usage of built-ins with higher arity (5 or 8) is not precluded in such a processor. After all, a conforming processor needs just to prepare Prolog text for execution (5.1.a) and correctly execute Prolog goals (5.1.b). Nowhere is it stated that such goals are represented as compound terms.
But of course that is what we expect, as we expect to be able to program our own top level loop which is itself out of scope of ISO/IEC 13211-1.
Ad 1: As for future portability, 255 appears to be the minimum. But IF has 127. Minerva 125. Of course unbounded would be the best choice, with up to 7 for fast and compact representation.
Are there clues I am missing?
The relation of compound terms to predicates is often overseen in this context. A system may support predicates with only a smaller arity than max_arity — the appropriate error to issue here would be a resource_error. Compare this situation to a system with current_prolog_flag(max_arity, unbounded) and the goal functor(F,f,N) with N exceeding the processor's address space (in current implementations). While such a goal would never succeed in current implementations, it still issues a resource_error and not a representation_error.
There is a gap somehow. max_arity only covers the compound size. But there is no flag for predicate size. The two things might vary as SWI-Prolog shows. But SWI-Prolog names the limit wrongly max_arity, although its not its max_arity flag:
/* SWI-Prolog 8.3.19 */
?- functor(_,f,10000).
true.
?- functor(F,f,10000), assertz(F).
ERROR: Cannot represent due to `max_arity' (limit is 1024, request = 10000)
?- current_prolog_flag(max_arity, X).
X = unbounded.
The predicate size limit might be more relevant to call/n than the compound size. The ISO core standard definition is a little unsatisfactory.
But its not thoroughly checked, but a fix on SWI-Prologs side is underway:
?- functor(F,f,10000), assertz(test:-F).
F = f(_ ...)
?- functor(F,f,10000), call(F,a).
%%% crash
Edit 25.02.2021:
The glitch in the error message is only explained when you look at the C-Code of SWI-Prolog, which has a C Constant MAXARITY with the meaning of predicate size. This Constant and the Error Message possibly predates the ISO core standard, which introduced another constant.
The ISO Prolog standard predicate with the larger number of arguments is sub_atom/5. We can use this predicate to justify at least call/6 and thus a maximum arity of at least 6:
| ?- call(sub_atom, A, B, C, D, E).
Of course, with call/6, nothing prevents the programmer to call e.g.
| ?- call(foo(A,B,C), D, E, F, G, H)
Related
In conversations around Prolog's cut operator !/0, I have only ever heard it described in terms of choice points and Prolog's execution model. It also seems to me that cut was introduced to give more "imperative" control over the search procedure, and to potentially improve the performance of some programs, but not so much for theoretical reasons. For examples of such discussions, see SWI Prolog's documentation, SICStus Prolog's documentation, and the Wikipedia article on cuts.
I'm curious if there are other ways of thinking about cuts in Prolog code? Specifically, can cut be given (possibly messy) declarative semantics in the language of first order logic? Second order logic? Higher order logic? If not, are there extensions to these logics that make it possible to implement cut?
There is no declarative semantics for the cut in the general case. It suffices to consider one counterexample:
?- X = 2, ( X = 1 ; X = 2 ), !.
X = 2.
?- ( X = 1 ; X = 2 ), !, X = 2.
false.
So once it is the case and once not. Therefore, conjunction is not commutative. But to have a declarative semantics, conjunction must be commutative. And even in Pure Prolog commutativity of conjunction holds1. So maybe there is some way to twist the meaning of declarative. But twisting it that far is just too much.
The only statement we can say is that in a program with cuts the search space is somehow pruned compared to the very same program without cuts. But usually that is not that interesting. Think of Prolog negation.
mynot(G_0) :-
G_0,
!,
false.
mynot(_G_0).
So this is compared to its cut free version where the fact will always ensure success. The meaning of negation is therefore lost in the cut free program.
However, some uses of cuts do have a declarative semantics. But those cuts are typically guarded to ensure that it does not cut too often. These guards may come in different styles. One possibility is to produce an instantiation error in case the arguments are not sufficiently instantiated. Another is to fail and resort to the original definition. Another is to ensure that arguments are sufficiently instantiated by some mode system. As long as such mode system is actually enforced it is a declarative option. However, if modes are only commentary decorum, the declarative meaning is lost.
1 Conjunction in Pure Prolog is commutative modulo termination and errors.
"Purely for performance"?
Cuts are convenient, as a sometimes-more-elegant alternative to if-then-else.
Main features
I recently have been looking to make a Prolog meta-interpreter with a certain set of features, but I am starting to see that I don't have the theoretical knowledge to work on it.
The features are as follows :
Depth-first search.
Interprets any non-recursive Prolog program the same way a classic interpreter would.
Guarantees breaking out of any infinite recursion. This most-likely means breaking Turing-completeness, and I'm okay with that.
As long as each step of the recursion reduces the complexity of the expression, keep evaluating it. To be more specific, I want predicates to be allowed to call themselves, but I want to prevent a clause to be able to call a similarly or more complex version of itself.
Obviously, (3) and (4) are the ones I am having problems with. I am not sure if those 2 features are compatible. I am not even sure if there is a way to define complexity such that (4) makes logical sense.
In my researches, I have come across the concept of "unavoidable pattern", which, I believe, provides a way to ensure feature (3), as long as feature (4) has a well-formed definition.
I specifically want to know if this kind of interpreter has been proven impossible, and, if not, if theoretical or concrete work on similar interpreters has been done in the past.
Extra features
Provided the above features are possible to implement, I have extra features I want to add, and would be grateful if you could enlighten me on the feasibility of such features as well :
Systematically characterize and describe those recursions, such that, when one is detected, a user-defined predicate or clause could be called that matches this specific form of recursion.
Detect patterns that result in an exponential number of combinatorial choices, prevent evaluation, and characterize them in the same way as step (5), such that they can be handled by a built-in or user-defined predicate.
Example
Here is a simple predicate that obviously results in infinite recursion in a normal Prolog interpreter in all but the simplest of cases. This interpreter should be able to evaluate it in at most PSPACE (and, I believe, at most P if (6) is possible to implement), while still giving relevant results.
eq(E, E).
eq(E, F):- eq(E,F0), eq(F0,F).
eq(A + B, AR + BR):- eq(A, AR), eq(B, BR).
eq(A + B, B + A).
eq(A * B, B * A).
eq((A * B) / B, A).
eq(E, R):- eq(R, E).
Example of results expected :
?- eq((a + c) + b, b + (c + a)).
true.
?- eq(a, (a * b) / C).
C = b.
The fact that this kind of interpreter might prove useful by the provided example hints me towards the fact that such an interpreter is probably impossible, but, if it is, I would like to be able to understand what makes it impossible (for example, does (3) + (4) reduce to the halting problem? is (6) NP?).
If you want to guarantee termination you can conservatively assume any input goal is nonterminating until proven otherwise, using a decidable proof procedure. Basically, define some small class of goals which you know halt, and expand it over time with clever ideas.
Here are three examples, which guarantee or force three different kinds of termination respectively (also see the Power of Prolog chapter on termination):
existential-existential: at least one answer is reached before potentially diverging
universal-existential: no branches diverge but there may be an infinite number of them, so the goal may not be universally terminating
universal-universal: after a finite number of steps, every answer will be reached, so in particular there must be a finite number of answers
In the following, halts(Goal) is assumed to correctly test a goal for existential-existential termination.
Existential-Existential
This uses halts/1 to prove existential termination of a modest class of goals. The current evaluator eval/1 just falls back to the underlying engine:
halts(halts(_)).
eval(Input) :- Input.
:- \+ \+ halts(halts(eval(_))).
safe_eval(Input) :-
halts(eval(Input)),
eval(Input).
?- eval((repeat, false)).
C-c C-cAction (h for help) ? a
abort
% Execution Aborted
?- safe_eval((repeat, false)).
false.
The optional but highly recommended goal directive \+ \+ halts(halts(eval(_))) ensures that halts will always halt when run on eval applied to anything.
The advantage of splitting the problem into a termination checker and an evaluator is that the two are decoupled: you can use any evaluation strategy you want. halts can be gradually augmented with more advanced methods to expand the class of allowed goals, which frees up eval to do the same, e.g. clause reordering based on static/runtime mode analysis, propagating failure, etc. eval can itself expand the class of allowed goals by improving termination properties which are understood by halts.
One caveat - inputs which use meta-logical predicates like var/1 could subvert the goal directive. Maybe just disallow such predicates at first, and again relax the restriction over time as you discover safe patterns of use.
Universal-Existential
This example uses a meta-interpreter, adapted from the Power of Prolog chapter on meta-interpreters, to prune off branches which can't be proven to existentially terminate:
eval(true).
eval((A,B)) :- eval(A), eval(B).
eval((A;_)) :- halts(eval(A)), eval(A).
eval((_;B)) :- halts(eval(B)), eval(B).
eval(g(Head)) :-
clause(Head, Body),
halts(eval(Body)),
eval(Body).
So here we're destroying branches, rather than refusing to evaluate the goal.
For improved efficiency, you could start by naively evaluating the input goal and building up per-branch sets of visited clause bodies (using e.g. clause/3), and only invoke halts when you are about to revisit a clause in the same branch.
Universal-Universal
The above meta-interpreter rules out at least all the diverging branches, but may still have an infinite number of individually terminating branches. If we want to ensure universal termination we can again do everything before entering eval, as in the existential-existential variation:
...
:- \+ \+ halts(halts(\+ \+ eval(_))).
...
safe_eval(Input) :-
halts(\+ \+ eval(Input)),
eval(Input).
So we're just adding in universal quantification.
One interesting thing you could try is running halts itself using eval. This could yield speedups, better termination properties, or qualitatively new capabilities, but would of course require the goal directive and halts to be written according to eval's semantics. E.g. if you remove double negations then \+ \+ would not universally quantify, and if you propagate false or otherwise don't conform to the default left-to-right strategy then the (goal, false) test for universal termination (PoP chapter on termination) also would not work.
I am currently trying to understand the basics of prolog.
I have a knowledge base like this:
p(a).
p(X) :- p(X).
If I enter the query p(b), the unification with the fact fails and the rule p(X) :- p(X) is used which leads the unification with the fact to fail again. Why is the rule applied over and over again after that? Couldn't prolog return false at this point?
After a certain time I get the message "Time limit exceeded".
I'm not quite sure why prolog uses the rule over and over again, but since it is, I don't understand why I get a different error message as in the following case.
To be clear, I do understand that "p(X) if p(X)" is an unreasonable rule, but I would like to understand what exactly happens there.
If I have a knowledge base like this:
p(X) :- p(X).
p(a).
There is no chance to come to a result even with p(a) because the fact is below the rule and the rule is called over and over again. For this variant I receive a different error message almost instantly "ERROR: Out of local stack" which is comprehensible.
Now my question - what is the difference between those cases?
Why am I receiving different error messages and why is prolog not returning false after the first application of the rule in the above case? My idea would be that in the above case the procedure is kind of restarted each time the rule gets called and in the below case the same procedure calls the rule over and over again. I would be grateful if somebody could elaborate this.
Update: If I query p(a). to the 2nd KB as said I receive "Out of local stack", but if I query p(b). to the same KB I get "Time limit exceeded". This is even more confusing to me, shouldn't the constant be irrelevant for the infinite loop?
Let us first consider the following program fragment that both examples have in common:
p(X) :- p(X).
As you correctly point out, it is obvious that no particular solutions are described by this fragment in isolation. Declaratively, we can read it as: "p(X) holds if p(X) holds". OK, so we cannot deduce any concrete solution from only this clause.
This explains why p(b) cannot hold if only this fragment is considered. Additionally, p(a) does not imply p(b) either, so no matter where you put the fact p(a), you will never derive p(b) from these two clauses.
Procedurally, Prolog still attempts to find cases where p(X) holds. So, if you post ?- p(X). as a query, Prolog will try to find a resolution refutation, disregarding what it has "already tried". For this reason, it will try to prove p(X) over and over. Prolog's default resolution strategy, SLDNF resolution, keeps no memory of which branches have already been tried, and also for this reason can be implemented very efficiently, with little overhead compared to other programming languages.
The difference between an infinite deduction attempt and an out of local stack error error can only be understood procedurally, by taking into account how Prolog executes these fragments.
Prolog systems typically apply an optimization that is called tail call optimization. This is applicable if no more choice-points remain, and means that it can discard (or reuse) existing stack frames.
The key difference between your two examples is obviously where you add the fact: Either before or after the recursive clause.
In your case, if the recursive clause comes last, then no more choice-points remain at the time the goal p(X) is invoked. For this reason, an existing stack frame can be reused or discarded.
On the other hand, if you write the recursive clause first, and then query ?- q(X). (or ?- q(a).), then both clauses are applicable, and Prolog remembers this by creating a choice-point. When the recursive goal is invoked, the choice-point still exists, and therefore the stack frames pile up until they exceed the available limits.
If you query ?- p(b)., then argument indexing detects that p(a) is not applicable, and again only the recursive clause applies, independent of whether you write it before or after the fact. This explains the difference between querying p(X) (or p(a)) and p(b) (or other queries). Note that Prolog implementations differ regarding the strength of their indexing mechanisms. In any case, you should expect your Prolog system to index at least on the outermost functor and arity of the first argument. If necessary, more complex indexing schemes can be constructed manually on top of this mechanism. Modern Prolog systems provide JIT indexing, deep indexing and other mechanisms, and so they often automatically detect the exact subset of clauses that are applicable.
Note that there is a special form of resolution called SLG resolution, which you can use to improve termination properties of your programs in such cases. For example, in SWI-Prolog, you can enable SLG resolution by adding the following directives before your program:
:- use_module(library(tabling)).
:- table p/1.
With these directives, we obtain:
?- p(X).
X = a.
?- p(b).
false.
This coincides with the declarative semantics you expect from your definitions. Several other Prolog systems provide similar facilities.
It should be easy to grasp the concept of infinite loop by studying how standard repeat/0 is implemented:
repeat.
repeat :- repeat.
This creates an infinite number of choice points. First clause, repeat., simply allows for a one time execution. The second clause, repeat :- repeat. makes it infinitely deep recursion.
Adding any number of parameters:
repeat(_, _, ..., _).
repeat(Param1, Param2, ..., ParamN) :- repeat(Param1, Param2, ..., ParamN).
You may have bodies added to these clauses and have parameters of the first class having meaningful names depending on what you are trying to archive. If bodies won't contain cuts, direct or inherited from predicates used, this will be an infinite loop too just as repeat/0.
?- X in 1..100.
X in 1..100.
?- X #> 0, X #< 101.
X in 1..100.
So far so good!
Now let's imagine that the sup bound of X depends on Y in 100..200.
?- Y in 100..200, X #> 0, X #=< Y.
Y in 100..200,
Y#>=X,
X in 1..200.
The domain of X propagates correctly to 1..200, with the constraint between X and Y.
But…
?- Y in 100..200, X in 1..Y.
ERROR: Arguments are not sufficiently instantiated
I thought that in was equivalent to the inequalities declaration, but it apparently is not.
Is there any hidden reason as to why in requires that both bounds are ground?
That's a very good question. It has a straight-forward answer that may at first appear deep, yet is really easy to understand once you are familiar with declarative debugging approaches that are beginning to become more widely available.
Declarative debugging relies on properties of logic programs that we know always hold. A key property of pure logic programs (see logical-purity) is monotonicity:
Generalizing a goal can at meast increase the set of solutions.
Here is an example:
?- append([a], [b], [c]).
false.
Why does this fail? We want to find an actual cause of failure, an explanation why this does not succeed. It is tempting to think in procedural terms and trace the execution, but this quickly gets extremely complex and does not really show us the essence of the reason.
Instead, we can get to the essence by generalizing the goals systematically. For example, the following succeeds:
?- append([a], [b], Cs).
I have generalized the query by replacing the term [c] with Cs, which subsumes it.
You can do this automatically, see Ulrich Neumerkel's library(diadem) and especially the GUPU system where these ideas are fully developed and help tremendously to locate the precise location of mistakes in Prolog programs.
To apply such techniques to their utmost extent, you must aim to preserve as many interesting properties as possible in your programs, essentially by staying in the pure monotonic core of Prolog. I say "essentially" because these techniques also work somewhat beyond this subset on a class of programs that is very hard to classify formally. Note also that much of the ongoing work on logic programming is aimed towards increasing the pure monotonic subset of Prolog as far as possible, so the restriction becomes less and less severe over time.
Consider now the specifics of (in)/2: The domain argument of (in)/2 carries within it a declarative flaw in that it uses a defaulty representation of domains. There are several ways to see this, and notably from the following situation: Suppose I tell you that a domain has the form inf..High, then what is High? It can be either of:
sup
an integer.
The fact that we cannot clearly distinguish these cases tells you that this representation is defaulty.
A clean representation of boundaries would look like this:
inf, sup to denote the infinities
n(N) to denote the integer N.
With such a representation, I could tell you that the domain looks like this: inf..n(N), and you know that N must be an integer.
Now another example:
?- X in 1..0.
false.
Why does this fail? Let us again generalize the arguments to find a reason:
?- X in Low..0.
Suppose now that (in)/2 accepted this as an admissible query. Then what is Low? Again, it can be either an integer, or the atom inf. Unfortunately, there is no sensible way to constrain Low in this way: Constraining it to integers would make CLP(FD) constraints most appropriate, making it tempting to answer with:
X in Low..0,
Low in inf..0
but that is of course too much, because it precludes Low = inf:
?- Low in inf..0, Low = inf.
Type error: `integer' expected, found `inf' (an atom)
Knowing that we cannot decide the type of the term Low in such situations, and lacking a sensible way to state this, an instantiation error is raised to indicate that such a generalization is inadmissible.
From these considerations, the overall answer is really simple:
(in)/2 cannot be easily generalized due to the defaulty representation of domains.
Invariably, there are only two reasons for using such representations:
they are easier to type
they are easier to read.
The hefty drawback of course is that they stand massively in the way of declarative reasoning over your programs.
Note that internally, the CLP(FD) system of SWI-Prolog does use a clean representation of domains. This can also be carried over to the user side, so that we could write:
?- X in n(Low)..0.
with the declaratively perfectly valid answer:
X in n(Low)..0,
Low in inf..0.
Over time, such cleaner notations will definitely find their way to users. For now, it would be a bit too much to ask, at least in my experience.
In the specific examples you cite, the domain boundary is already constrained to integers, so of course we can distinguish the cases and translate this automatically to inequalitites. However, this would not remove the core problem of defaultyness, and exchanging the goals would still raise an instantiation error.
I often end up writing code in Prolog which involves some arithmetic calculation (or state information important throughout the program), by means of first obtaining the value stored in a predicate, then recalculating the value and finally storing the value using retractall and assert because in Prolog we cannot assign values to variable twice using is (thus making almost every variable that needs modification, global). I have come to know that this is not a good practice in Prolog. In this regard I would like to ask:
Why is it a bad practice in Prolog (though i myself don't like to go through the above mentioned steps just to have have a kind of flexible (modifiable) variable)?
What are some general ways to avoid this practice? Small examples will be greatly appreciated.
P.S. I just started learning Prolog. I do have programming experience in languages like C.
Edited for further clarification
A bad example (in win-prolog) of what I want to say is given below:
:- dynamic(value/1).
:- assert(value(0)).
adds :-
value(X),
NewX is X + 4,
retractall(value(_)),
assert(value(NewX)).
mults :-
value(Y),
NewY is Y * 2,
retractall(value(_)),
assert(value(NewY)).
start :-
retractall(value(_)),
assert(value(3)),
adds,
mults,
value(Q),
write(Q).
Then we can query like:
?- start.
Here, it is very trivial, but in real program and application, the above shown method of global variable becomes unavoidable. Sometimes the list given above like assert(value(0))... grows very long with many more assert predicates for defining more variables. This is done to make communication of the values between different functions possible and to store states of variables during the runtime of program.
Finally, I'd like to know one more thing:
When does the practice mentioned above become unavoidable in spite of various solutions suggested by you to avoid it?
The general way to avoid this is to think in terms of relations between states of your computations: You use one argument to hold the state that is relevant to your program before a calculation, and a second argument that describes the state after some calculation. For example, to describe a sequence of arithmetic operations on a value V0, you can use:
state0_state(V0, V) :-
operation1_result(V0, V1),
operation2_result(V1, V2),
operation3_result(V2, V).
Notice how the state (in your case: the arithmetic value) is threaded through the predicates. The naming convention V0 -> V1 -> ... -> V scales easily to any number of operations and helps to keep in mind that V0 is the initial value, and V is the value after the various operations have been applied. Each predicate that needs to access or modify the state will have an argument that allows you to pass it the state.
A huge advantage of threading the state through like this is that you can easily reason about each operation in isolation: You can test it, debug it, analyze it with other tools etc., without having to set up any implicit global state. As another huge benefit, you can then use your programs in more directions provided you are using sufficiently general predicates. For example, you can ask: Which initial values lead to a given outcome?
?- state0_state(V0, given_outcome).
This is of course not readily possible when using the imperative style. You should therefore use constraints instead of is/2, because is/2 only works in one direction. Constraints are much easier to use and a more general modern alternative to low-level arithmetic.
The dynamic database is also slower than threading states through in variables, because it performs indexing etc. on each assertz/1.
1 - it's bad practice because destroys the declarative model that (pure) Prolog programs exhibit.
Then the programmer must think in procedural terms, and the procedural model of Prolog is rather complicate and difficult to follow.
Specifically, we must be able to decide about the validity of asserted knowledge while the programs backtracks, i.e. follow alternative paths to those already tried, that (maybe) caused the assertions.
2 - We need additional variables to keep the state. A practical, maybe not very intuitive way, is using grammar rules (a DCG) instead of plain predicates. Grammar rules are translated adding two list arguments, normally hidden, and we can use those arguments to pass around the state implicitly, and reference/change it only where needed.
A really interesting introduction is here: DCGs in Prolog by Markus Triska. Look for Implicitly passing states around: you'll find this enlighting small example:
num_leaves(nil), [N1] --> [N0], { N1 is N0 + 1 }.
num_leaves(node(_,Left,Right)) -->
num_leaves(Left),
num_leaves(Right).
More generally, and for further practical examples, see Thinking in States, from the same author.
edit: generally, assert/retract are required only if you need to change the database, or keep track of computation result along backtracking. A simple example from my (very) old Prolog interpreter:
findall_p(X,G,_):-
asserta(found('$mark')),
call(G),
asserta(found(X)),
fail.
findall_p(_,_,N) :-
collect_found([],N),
!.
collect_found(S,L) :-
getnext(X),
!,
collect_found([X|S],L).
collect_found(L,L).
getnext(X) :-
retract(found(X)),
!,
X \= '$mark'.
findall/3 can be seen as the basic all solutions predicate. That code should be the very same from Clockins-Mellish textbook - Programming in Prolog. I used it while testing the 'real' findall/3 I implemented. You can see that it's not 'reentrant', because of the '$mark' aliased.