Related
When I was writing down this question on an empty list as a difference list I wanted to test what I knew about those structures. However, when I tried something as simple as comparing different notations it seemed that I was wrong and that I did not understand what is actually going on with difference lists.
?- L = [a,b,c|[d,e]]-[d,e], L = [a,b,c].
false % expected true
I tested this on SWI-Prolog as well as SICStus. I verified the notation as this is how it is written in Bratko's Prolog Programming for AI, page 210, but apparently unification is not possible. Why is that? Don't these notations have the same declarative meaning?
I think you have the idea that the Prolog interpreter treats difference lists as something special. That is not the case: Prolog is not aware of the concept of a difference list (nor of nearly every concept except some syntactical sugar). He only sees:
L=-( |(a, |(b, |(c, |(d, |(e, []))))), |(d, |(e, [] )))
where -/2 and |/2 are functors, and a, b, c, d, e and [] are constants.
Difference lists are simply a programming technique (like for instance dynamic programming is a technique as well, the compiler cannot detect nor treat dynamic programming programs differently). It is used to efficiently unify a (partially) ununified part deep in an expression.
Say you want to append/3 two lists. You can do this as follows:
%append(A,B,C).
append([],L,L).
append([H|T],L,[H|B]) :-
append(T,L,B).
But this runs in O(n): you first need to iterate through the entire first list. If that list contains thousands of elements, it will take a lot of time.
Now you can define yourself a contract that you will feed an append_diff/3 not only the list, but a tuple -(List,Tail) where List is a reference to the beginning of the list, and Tail is a reference to the end of the not unified list. Examples of structures that fulfill this requirement are Tail-Tail, [a|Tail]-Tail, [1,4,2,5|Tail]-Tail.
Now you can effectively append_diff/3 in O(1) with:
append_diff(H1-T1,T1-T2,H1-T2).
Why? Because you unify the ununified tail of the first list with the second list. Now the ununified tail of the second lists becomes the tail of the final list. So take for instance:
append_diff([a|T1]-T1,[1,4,2,5|T2]-T2,L).
If you call the predicate, as you see above, T1 will unify with [1,4,2,5|T2], so now the first list collapses to [a|[1,4,2,5|T2]] or shorter [a,1,4,2,5|T2], since we also have a reference to T2, we can "return" (in Prolog nothing is returned), [a,1,4,2,5|T2]-T2: a new difference list with an open tail T2. But this is only because you give - a special meaning yourself: for Prolog - is simply -, it is not minus, it does not calculate a difference, etc. Prolog does not attach semantics to functors. If you would have used + instead of -, that would not have made the slightest difference.
So to return back to your question: you simply state to Prolog that L = -([a,b,c,d,e],[d,e]) and later state that L = [a,b,c]. Now it is clear that those two expressions cannot be unified. So Prolog says false.
In Prolog, [H|T] is the list that begins with H and where the remaining elements are in the list T (internally represented with '.'(H, '.'(…))).
Is it possible to define new syntax in a similar fashion? For example, is it possible to define that [T~H] is the list that ends with H and where the remaining elements are in the list T, and then use it as freely as [H|T] in heads and bodies of predicates? Is it also possible to define e.g. <H|T> to be a different structure than lists?
One can interpret your question literally. A list-like data structure, where accessing the tail can be expressed without any auxiliary predicate. Well, these are the minus-lists which were already used in the very first Prolog system — the one which is sometimes called Prolog 0 and which was written in Algol-W. An example from the original report, p.32 transliterated into ISO Prolog:
t(X-a-l, X-a-u-x).
?- t(nil-m-e-t-a-l, Pluriel).
Pluriel = nil-m-e-t-a-u-x.
So essentially you take any left-associative operator.
But, I suspect, that's not what you wanted. You probably want an extension to lists.
There have been several attempts to do this, one more recent was Prolog III/Prolog IV. However, quite similar to constraints, you will have to face how to define equality over these operators. In other words, you need to go beyond syntactic unification into E-unification. The problem sounds easy in the beginning but it is frightening complex. A simple example in Prolog IV:
>> L = [a] o M, L = M o [z].
M ~ list,
L ~ list.
Clearly this is an inconsistency. That is, the system should respond false. There is simply no such M, but Prolog IV is not able to deduce this. You would have to solve at least such problems or get along with them somehow.
In case you really want to dig into this, consider the research which started with J. Makanin's pioneering work:
The Problem of Solvability of Equations in a Free Semi-Group, Akad. Nauk SSSR, vol.233, no.2, 1977.
That said, it might be the case that there is a simpler way to get what you want. Maybe a fully associative list operator is not needed.
Nevertheless, do not expect too much expressiveness from such an extension compared to what we have in Prolog, that is DCGs. In particular, general left-recursion would still be a problem for termination in grammars.
It is possible to extend or redefine syntax of Prolog with iso predicate
:- op(Precedence, Type, Name).
Where Precedence is a number between 0 and 1200, Type describe if the operatot is used postfix,prefix or infix:
infix: xfx, xfy, yfx
prefix: fx, fy
suffix: xf, yf
and finally name is the operator's name.
Operator definitions do not specify the meaning of an operator, but only describe how it can be used syntactically. It is only a definition extending the syntax of Prolog. It doesn't gives any information about when a predicate will succeed. So you need also to describe when your predicate succeeds. To answer your question and also give an example you could define :
:- op( 42, xfy, [ ~ ]).
where you declare an infix operator [ ~ ]. This doesn't means that is a representation of a list (yet). You could define clause:
[T ~ H]:-is_list([H|T]).
which matches [T~H] with the list that ends with H and where the remaining elements are in the list T.
Note also that it is not very safe to define predefined operators
like [ ] or ~ because you overwrite their existing functionality.
For example if you want to consult a file like [file]. this will
return false because you redefined operators.
Is there any logical difference between these two implementations of a variant predicate?
variant1(X,Y) :-
subsumes_term(X,Y),
subsumes_term(Y,X).
variant2(X_,Y_) :-
copy_term(X_,X),
copy_term(Y_,Y),
numbervars(X, 0, N),
numbervars(Y, 0, N),
X == Y.
Neither variant1/2 nor variant2/2 implement a test for being a syntactic variant. But for different reasons.
The goal variant1(f(X,Y),f(Y,X)) should succeed but fails. For some cases where the same variable appears on both sides, variant1/2 does not behave as expected. To fix this, use:
variant1a(X, Y) :-
copy_term(Y, YC),
subsumes_term(X, YC),
subsumes_term(YC, X).
The goal variant2(f('$VAR'(0),_),f(_,'$VAR'(0))) should fail but succeeds. Clearly, variant2/2 assumes that no '$VAR'/1 occur in its arguments.
ISO/IEC 13211-1:1995 defines variants as follows:
7.1.6.1 Variants of a term
Two terms are variants if there is a bijection s of the
variables of the former to the variables of the latter such that
the latter term results from replacing each variable X in the
former by Xs.
NOTES
1 For example, f(A, B, A) is a variant of f(X, Y, X),
g(A, B) is a variant of g(_, _), and P+Q is a variant of
P+Q.
2 The concept of a variant is required when defining bagof/3
(8.10.2) and setof/3 (8.10.3).
Note that the Xs above is not a variable name but rather (X)s. So s is here a bijection, which is a special case of a substitution.
Here, all examples refer to typical usages in bagof/3 and setof/3 where variables happen to be always disjoint, but the more subtle case is when there are common variables.
In logic programming, the usual definition is rather:
V is a variant of T iff there exists σ and θ such that
Vσ and T are identical
Tθ and V are identical
In other words, they are variants if both match each other. However, the notion of matching is pretty alien to Prolog programmers, that is, the notion of matching as used in formal logic. Here is a case which lets many Prolog programmers panic:
Consider f(X) and f(g(X)). Does f(g(X)) match f(X) or not? Many Prolog programmers will now shrug their shoulders and mumble something about the occurs-check. But this is entirely unrelated to the occurs-check. They match, yes, because
f(X){ X ↦ g(X) } is identical to f(g(X)).
Note that this substitution replaces all X and substitutes them for g(X). How can this happen? In fact, it cannot happen with Prolog's typical term representation as a graph in memory. In Prolog the node X is a real address somehow in memory, and you cannot do such an operation at all. But in logic things are on an entirely textual level. It's just like
sed 's/\<X\>/g(X)/g'
except that one can also replace variables simultaneously. Think of { X ↦ Y, Y ↦ X}. They have to be replaced at once, otherwise f(X,Y) would shrink into f(X,X) or f(Y,Y).
So this definition, while formally perfect, relies on notions that have no direct correspondence in Prolog systems.
Similar problems happen when one-sided unification is considered which is not matching, but the common case between unification and matching.
According to ISO/IEC 13211-1:1995 Cor.2:2012 (draft):
8.2.4 subsumes_term/2
This built-in predicate provides a test for syntactic one-sided unification.
8.2.4.1 Description
subsumes_term(General, Specific) is true iff there is a
substitution θ such
that
a) Generalθ
and Specificθ are identical, and
b) Specificθ and Specific
are identical.
Procedurally, subsumes_term(General, Specific) simply
succeeds or fails accordingly. There is no side effect or
unification.
For your definition of variant1/2, subsumes_term(f(X,Y),f(Y,X)) already fails.
Because Prolog uses chronological backtracking(from the Prolog Wikipedia page) even after an answer is found(in this example where there can only be one solution), would this justify Prolog as using eager evaluation?
mother_child(trude, sally).
father_child(tom, sally).
father_child(tom, erica).
father_child(mike, tom).
sibling(X, Y) :- parent_child(Z, X), parent_child(Z, Y).
parent_child(X, Y) :- father_child(X, Y).
parent_child(X, Y) :- mother_child(X, Y).
With the following output:
?- sibling(sally, erica).
true ;
false.
To summarize the discussion with #WillNess below, yes, Prolog is strict. However, Prolog's execution model and semantics are substantially different from the languages that are usually labelled strict or non-strict. For more about this, see below.
I'm not sure the question really applies to Prolog, because it doesn't really have the kind of implicit evaluation ordering that other languages have. Where this really comes into play in a language like Haskell, you might have an expression like:
f (g x) (h y)
In a strict language like ML, there is a defined evaluation order: g x will be evaluated, then h y, and f (g x) (h y) last. In a language like Haskell, g x and h y will only be evaluated as required ("non-strict" is more accurate than "lazy"). But in Prolog,
f(g(X), h(Y))
does not have the same meaning, because it isn't using a function notation. The query would be broken down into three parts, g(X, A), h(Y, B), and f(A,B,C), and those constituents can be placed in any order. The evaluation strategy is strict in the sense that what comes earlier in a sequence will be evaluated before what comes next, but it is non-strict in the sense that there is no requirement that variables be instantiated to ground terms before evaluation can proceed. Unification is perfectly content to complete without having given you values for every variable. I am bringing this up because you have to break down a complex, nested expression in another language into several expressions in Prolog.
Backtracking has nothing to do with it, as far as I can tell. I don't think backtracking to the nearest choice point and resuming from there precludes a non-strict evaluation method, it just happens that Prolog's is strict.
That Prolog pauses after giving each of the several correct answers to a problem has nothing to do with laziness; it is a part of its user interaction protocol. Each answer is calculated eagerly.
Sometimes there will be only one answer but Prolog doesn't know that in advance, so it waits for us to press ; to continue search, in hopes of finding another solution. Sometimes it is able to deduce it in advance and will just stop right away, but only sometimes.
update:
Prolog does no evaluation on its own. All terms are unevaluated, as if "quoted" in Lisp.
Prolog will unfold your predicate definitions as written and is perfectly happy to keep your data structures full of unevaluated uninstantiated holes, if so entailed by your predicate definitions.
Haskell does not need any values, a user does, when requesting an output.
Similarly, Prolog produces solutions one-by-one, as per the user requests.
Prolog can even be seen to be lazier than Haskell where all arithmetic is strict, i.e. immediate, whereas in Prolog you have to explicitly request the arithmetic evaluation, with is/2.
So perhaps the question is ill-posed. Prolog's operations model is just too different. There are no "results" nor "functions", for one; but viewed from another angle, everything is a result, and predicates are "multi"-functions.
As it stands, the question is not correct in what it states. Chronological backtracking does not mean that Prolog will necessarily backtrack "in an example where there can be only one solution".
Consider this:
foo(a, 1).
foo(b, 2).
foo(c, 3).
?- foo(b, X).
X = 2.
?- foo(X, 2).
X = b.
So this is an example that does have only one solution and Prolog recognizes that, and does not attempt to backtrack. There are cases in which you can implement a solution to a problem in a way that Prolog will not recognize that there is only one logical solution, but this is due to the implementation and is not inherent to Prolog's execution model.
You should read up on Prolog's execution model. From the Wikipedia article which you seem to cite, "Operationally, Prolog's execution strategy can be thought of as a generalization of function calls in other languages, one difference being that multiple clause heads can match a given call. In that case, [emphasis mine] the system creates a choice-point, unifies the goal with the clause head of the first alternative, and continues with the goals of that first alternative." Read Sterling and Shapiro's "The Art of Prolog" for a far more complete discussion of the subject.
from Wikipedia I got
In eager evaluation, an expression is evaluated as soon as it is bound to a variable.
Then I think there are 2 levels - at user level (our predicates) Prolog is not eager.
But it is at 'system' level, because variables are implemented as efficiently as possible.
Indeed, attributed variables are implemented to be lazy, and are rather 'orthogonal' to 'logic' Prolog variables.
If you have for instance next line of Prolog declaration:
move(state(middle, onbox, middle, hasnot),
grasp,
state(middle, onbox, middle, has)).
Are both move and state functors?
I'm kind off confused by facts, functors, terms, ...
In Prolog functors are syntactic elements we use to build structures (compound terms) from simpler ones.
Think of a hierarchy of bound Prolog terms, with the base containing the simplest "atomic" cases, i.e. atoms and numbers. Add to these Prolog variables, which may be bound or not depending on context. The rules for Prolog functor names (identifiers) are the same as for Prolog atoms
Functors are syntactic units that have a finite number of arguments ("arity"), and if a functor is supplied with terms for those arguments, then we get a compound term. In your example there is a principal functor move with three arguments, so its arity is 3. The functor name and arity are often combined, since technically Prolog treats the same functor name with two distinct arities as different functors, and so we might refer to move/3 as the outer functor of your compound term.
Note that the first and third arguments in your example of a term are themselves compound terms, built using functor state/4:
move(state(middle, onbox, middle, hasnot),
grasp,
state(middle, onbox, middle, has))
Here I removed the period from the end of your example. Likely in its original context this was indeed a Prolog "fact", although it might also have been a query. Periods in Prolog can serve terminate input of a term.
The key point here is that Prolog predicates are special cases of functors. If move/3 is a predicate (taking three arguments), then your example could be a fact (if it is asserted someplace in your application) or a query (e.g. if posed as a top-level goal). In accordance with what we said above about a functor name with two distinct arities, Prolog treats a predicate name with different arities as different predicates!
Prolog's use of functors to express facts (and rules) makes it a fairly slick and powerful metaprogramming environment. Prolog has a special built-in predicate (operator =..) called univ that unpacks a compound term into a list whose head is the principal functor's name and whose remaining items are the arguments given to that functor in the specific compound term. For example:
?- X = state(middle, onbox, middle, hasnot), X =.. List.
X = state(middle, onbox, middle, hasnot)
List = [state, middle, onbox, middle, hasnot]
yes
This allows us to convert between Prolog lists and compound terms in both directions, and to build Prolog goals "dynamically" if need be (using a predicate name for the principal functor of a term).
Yes, move and state are functors. A functor is F in F(Term1, ...). But they aren't facts: in your case, there is only one fact, which is the complete line.
Functors describe a term, but are not the term itself (like method signatures in imperative languages) and consist of the structure's or predicate's name and arity.
In you example, the functors are move/3 and state/4.
Notice that foo(a) and foo(a,b) have different functors, foo/1 and foo/2.