A Declarative Semantics for the Prolog Cut Operator Karl Stroetmann∗and Thomas Glaß Siemens AG, Corporate Research and Development, D–81730 M¨ unchen, Germany June 12, 2013
Abstract In practice, a large part of most Prolog programs is in fact functional, the search facilities of Prolog are only rarely needed. In order to arrive at efficient implementations it is then convenient to make use of the cut operator provided by Prolog. Up to now, this operator has been regarded as an extra–logical control operator that destroys the declarative semantics of Prolog. In this paper we will show that this is not true. If the cut is used to implement functional predicates and if, furthermore, a certain discipline in its use is adhered to, then programs using the cut operator do possess a declarative semantics.
1
Introduction
It has been observed by others that in practice the main differences between programs written using the paradigm of functional programming and programs written using the paradigm of logic programming is often mainly syntactical. The reason is that a large number of predicates implemented in logic programming are but codings of the graph of a function. However, while an implementation of a function in a functional programming language has a declarative semantics, this is usually not the case for the implementation of a function in Prolog. This is due to the fact that in order to arrive at an efficient implementation it is convenient to make use of the cut operator. In general, the operational semantics of this operator is quite complicated, cf. [PS95, And95]. Nevertheless, if the use of the cut operator satisfies certain restrictions, then it is possible to give a simple declarative semantics to programs using the cut. The basic idea can best be understood in the propositional case. If p ← l1 ∧ · · · ∧ lm ∧ ! ∧ h1 ∧ · · · ∧ hn (1) ∗ Address
correspondence to
[email protected].
1
is a propositional Prolog clause, i.e. p is a null–ary relation symbol and li and hj are propositional literals, that is null–ary relation symbols or negated null–ary relation symbols, then the declarative semantics of this clause is given by the formula l1 ∧ · · · ∧ lm → p ↔ h1 ∧ · · · ∧ hn . (2) The intuitive reason that (2) correctly represents (1) is that • provided the clause (1) is selected to resolve a goal of the form p, and • the resolution of its conditional part l1 ∧ · · · ∧ lm is successful, then p is recognized as true if and only if the resolution of h1 ∧ · · · ∧ hn is successful. Since this declarative semantics does not reflect the ordering of clauses it is only correct if the operational semantics of the program under consideration is invariant under arbitrary reordering of its clauses. Another condition that has to be satisfied for our semantics to be correct is that the definitions of predicates are complete. In the propositional case this means that there must exist a clause in the definition of p such that the resolution of the conditional part of this clause is successful. The notion of completeness will be defined formally after we have made the operational semantics precise.
1.1
Comparison with Other Work
The cut operator has long been recognized as a problematic feature of Prolog, cf. the book of Lloyd [Llo87]. Previous work was mainly concerned with the formalization of the operational semantics and the development of a denotational semantics cf., among others, the works of Jones and Mycroft [JM84] and de Bruin and de Vink [dBdV89]. Another approach was followed by B¨orger who used the notion of dynamic algebras to give a mathematical description of the control facilities of Prolog [B¨or89]. To our knowledge, the only attempts to give a declarative semantics to the cut operator were the works by Voda [Vod88] and Andrews [And95]. Both works clearly show that, in the general case, the formulas describing a program using the cut operator are very complicated. This is reflected in the fact that in the declarative semantics given by Andrews the resulting formulas are not even computable! We can do much better because we do not cover the general case. In order to still have a theory that is applicable to real programs we have strived to define a subclass of full Prolog satisfying the following requirements: • The subclass should be large enough to cover the programming style used in practice. • The resulting declarative semantics should be simple. 2
Concerning the first requirement we have identified a functional programming style as the main source of the use of cuts. After all, when coding functions the resulting predicates have to be deterministic and that can most easily by achieved through the use of cuts. Fortunately, it is this functional programming style that has a simple declarative semantics! As a matter of fact, the declarative semantics is not only simple from a conceptual point of view but, furthermore, offers the possibility of applying formal methods to the process of validation. Since this process can be automated to a large extend it makes the application of formal methods economically viable. We will discuss this in more detail in the last section of this paper. Overview: In the next section we will introduce the necessary notions and notations used in the rest of this paper. In particular, we introduce modes and types. These are necessary prerequisites for introducing our version of an operational semantics for Prolog. This operational semantics will be introduced in Section 3. In Section 4 we give the declarative semantics of Prolog clauses containing the cut operator and, furthermore, prove this semantics to be consistent by constructing a model. In Section 5, this semantics is then shown to be sound and complete with respect to the operational semantics introduced previously. Finally, Section 6 discusses applications of this semantics.
2
Preliminaries
All notions and notations not defined in this paper are assumed to be the same as in Apt [Apt90]. We will only deviate in a small number of points from [Apt90], the most important being that we use modes and types which will be defined below. Another deviation is the fact that we do not use goals but rather find it more convenient to work with queries. A query is taken to be a conjunction of literals. The letters B, C, and G, possibly indexed, will range over queries. Furthermore, we will use the following meta rule for denoting sequences of items: If the items from a class are denoted by a fixed group of letters, then sequences of these items will be denoted by the same letters in boldface. For example, since s and t denote terms, s and t will be used to denote sequences of terms.
2.1
Modes and Types
As already noted by others, compare e. g. [Apt93], in order to develop a declarative semantics that is applicable to the programming style used in the practice of Prolog programming, one has to introduce the concept of modes [DM85] and types [MO84]. We find it convenient to use the notion of a well–typed program as given in Apt and Etalle [AE93], which is based on [BLR92]. We start with the notion of a mode used to define input and output positions of a relation.
3
Definition 1 (mode) A mode for an n–ary relation symbol p is a function mp from the set {1, · · · , n} to the set {+, −}. If mp (i) = “+”, we call i an input position of p and if mp (i) = “−”, we call i an output position of p. A moding is a collection of modes, each for a different relation symbol. 2 We proceed to define types. Since we do not intend to present a new type system here, the only assumption we make is that a type is a set of ground Prolog terms. The set of all types will be denoted by T. Individual types will be written as σ and τ . Definition 2 (type) A type for an n–ary relation symbol p is a function tp from the set {1, · · · , n} to the set T. If tp (i) = τ , we call τ the type associated with the position i of p. A typing is a collection of types, each for a different relation symbol. 2 The mode and the type of a relation are the components of the signature of the relation. Definition 3 (signature) A signature for an n–ary relation symbol p is a function sp from the set {1, · · · , n} to the set {+, −} × T such that sp (i) is the pair hmp (i), tp (i)i, where mp and tp are the mode and the type of the relation p. Instead of the clumsy hmp (i), tp (i)i we will write sp (i) as the string mp (i)tp (i). A signature for a Prolog program P is a collection of signatures for the relation symbols of P. 2 If p is an n–ary relation with mode mp and type tp , the signature of p will be written as p( mp (1) tp (1), · · ·, mp (n) tp (n) ). For example, the signature of the predicate sort/2 that takes a list of integers as first argument and produces a sorted version of this list in its second argument is written as sort( + int*, - int* ). Here int* denotes the type of all lists of integers. To be able to define the concept of a well–typed program we need the notion of a typed term. This is a construct of the form s : σ where s is a term and σ is a type. Given a sequence s1 : σ1 , · · · , sn : σn of typed terms we write s ∈ σ where s abbreviates the sequence s1 , · · · , sn and σ abbreviates the sequence σ1 , · · · , σn iff for all i = 1, · · · , n we have si ∈ σi . A type judgment is a statement of the form s : σ ⇒ t : τ . It is considered to be true (notation |= s : σ ⇒ t : τ ) iff for all substitutions ϑ, sϑ ∈ σ implies tϑ ∈ τ . In the definitions below we will write l to denote either a predicate symbol p or a negated predicate symbol ¬p. We will assume that if a relation p is used negatively, i.e. as ¬p(s), then p has only input positions, i.e. its signature has the form p(+σ1 , · · · , +σm ). For notational convenience we make the following agreement: If an atom is written as p(s, t), then the terms s are at input 4
positions while the terms t are at output positions, i.e. without loss of generality we make the assumption that the input positions always precede the output positions. Definition 4 (well–signed) In the following, assume that for i = 1, · · · , n the signature of the relation symbol pi is given by pi ( +σi , −τi ) while the signature of p0 is p0 ( +τ0 , −σn+1 ). • A clause p0 (t0 , sn+1 ) ← l1 (s1 , t1 ) ∧ · · · ∧ ln (sn , tn ) is well–signed iff for all i ∈ {1, · · · , n + 1} the following conditions are satisfied : 1. var(si ) ⊆
i−1 S
var(tj ).
j=0
2. |= t0 : τ0 , · · · , ti−1 : τi−1 ⇒ si : σi . • A query l1 (s1 , t1 ) ∧ · · · ∧ ln (sn , tn ) is well–signed iff the clause true ← l1 (s1 , t1 ) ∧ · · · ∧ ln (sn , tn ) is well–signed. • Finally, a program is called well–signed iff every clause of it is.
2
We need one further definition. Definition 5 (functional) A clause is called functional iff its body contains exactly one occurrence of the cut operator !, i.e. a clause is functional if it has the form P ← B1 ∧ ! ∧ B2 where B1 and B2 are queries such that the cut operator ! does not occur in them. A predicate p is functional iff every clause of its definition is functional. 2 Important: In the following we will only be interested in the functional part of a program, i.e. in that part that defines the functional predicates. Our experience has shown that this part makes up the bulk of most programs. In the following, we will use the letter P to always denote a functional program, i.e. a program consisting solely of functional predicates.
3
An Operational Semantics for Prolog
In this section we give a simple operational semantics for Prolog that captures the meaning of the cut operator. In order to prevent this semantics from getting to complicated, it will neither reflect the depth first search strategy nor the selection of clauses in Prolog. It will, however, reflect the left first selection rule of Prolog. Therefore, this semantics is only applicable for programs that are 5
universally terminating, complete, and confluent. These notions will be defined formally after we have defined the auxiliary relation →P . Informally, a program is universally terminating iff even after arbitrary reordering of the clauses there are no infinite computations. We will define the notion of completeness in a way that failure is always explicit failure, i.e. if a well–signed query of the form p(s, t) fails, then this is due to the fact that a clause p(l, r) ← B1 ∧ ! ∧ B2 has been selected such that µ = mgu(s, l) exists, the Prolog resolution of B1 µ has succeeded with answer ϑ, but the Prolog resolution of the query B2 µϑ ∧ tµϑ = rµϑ has failed. Therefore, explicit failure is always due to the fact that during the Prolog resolution of a query a cut–fail combination has been encountered. The notion of confluence will be defined so that it is ensured that an arbitrary reordering of the clauses of a program does not change its operational semantics. To start with we define the notion of a frame. A frame is a pair hG, ϑi where G is a query and ϑ is a substitution. A frame hG, ϑi is called well–signed iff the query Gϑ is well–signed. Given a program P, we proceed to define a rewrite relation →P on well–signed frames as follows: 1. Assume that hp(s, t), ϑi is a frame where the relation symbol p is different from the equality symbol =. Then, if P contains a (suitably renamed) clause p(l, r) ← B1 ∧ ! ∧ B2 such that hl = sϑ ∧ B1 , idi →∗P htrue, ηi holds, then we have hp(s, t), ϑi →P hB2 η ∧ t = rη, ϑi. Here, id denotes the identity substitution and t = rη is short for the formula t1 = r1 η ∧ · · · ∧ tn = rn η, where the assumption is made that r = r1 , · · · , rn and t = t1 , · · · , tn . Of course, a similar remark holds for l = sϑ. 2. If sϑ and tϑ are unifiable with most general unifier µ such that µ is idempotent and relevant in the sense of Lassez and Maher [LM87], then hs = t, ϑi →P htrue, ϑµi. 3. If sϑ and tϑ are not unifiable, then hs = t, ϑi →P hfalse, ϑi. 4. If hQ, ϑi →∗P hfalse, ϑηi, then 6
h¬Q, ϑi →P htrue, ϑi. 5. If hQ, ϑi →∗P htrue, ϑηi, then h¬Q, ϑi →P hfalse, ϑi. 6. We have htrue ∧ G, ϑi →P hG, ϑi. 7. We have hfalse ∧ G, ϑi →P hfalse, ϑi. 8. If L is a literal and hL, ϑi →∗P hG1 , ϑηi where G1 ∈ {true, false}, then hL ∧ G2 , ϑi →P hG1 ∧ G2 , ϑηi. For the following we need the fact that well–signedness is an invariant of the relation →P , i.e. if the frame F1 is well–signed and F1 →P F2 , then F2 is well–signed,1 too. This fact can be proven in the same way as the persistence of well–typedness under Prolog resolution in [BLR92]. Furthermore, we can prove that if hG1 , ϑ1 i is well–signed, then hG1 , ϑ1 i →P htrue, ϑ1 ϑ2 i implies that ϑ2 has the form {x1 /s1 , · · · , xn /sn } where si is ground for i = 1, · · · , n and var(Gϑ1 ) ⊆ {x1 , · · · , xn }. Therefore, if hG1 , ϑ1 i →P hG2 , ϑ1 ϑ2 i holds, then the substitution ϑ2 is ground. We proceed to make the notion of universal termination precise. We call a program P universally terminating iff there are no infinite sequences hG1 , ϑ1 i →P hG2 , ϑ2 i →P · · · →P hGn , ϑn i →P hGn+1 , ϑn+1 i →P · · · starting with a well–signed frame hG1 , ϑ1 i. The fact that a given program P is universally terminating implies that every well–signed query has a normal form. Here a frame F is in normal form iff there is no frame F1 such that F →P F1 holds. A frame Fˆ is a normal form of a frame F iff Fˆ is in normal form and, furthermore, F →∗P Fˆ . We call the program P confluent iff the rewrite relation does not fork, i.e. if hG, ϑi →P hG1 , ϑ1 i and hG, ϑi →P hG2 , ϑ2 i is only possible if G1 = G2 and ϑ1 = ϑ2 . Since there are no clauses defining true or false this is equivalent to the fact that for a given well–signed atom p(s, t) there exists at most one clause p(l, r) ← B1 ∧ ! ∧ B2 such that hl = s ∧ B1 , idi →∗P htrue, ηi 1 The attentive reader might have noticed that in order for this statement to make sense, the equality predicate =/2 has to be equipped with a signature. This can be done by defining the signature of =/2 as
’=’( -T, +T ) where T is a type parameter. This is correct since for every frame of the form hs = t, ϑi that is encountered, tϑ is a ground term.
7
holds for a suitable substitution η. If a program P is confluent, then normal forms are unique. Therefore, in this case we are allowed to speak of the normal form of a frame. Furthermore, the confluence of a program ensures that an arbitrary reordering of its clauses does not change its operational semantics: After all, although there might be different clauses that can be selected to resolve a given atom, there is at most one clause such that the control will reach the cut operator of this clause. Thus, in the case of confluent programs, reordering the clauses is merely a matter of efficiency. Finally, a program P is called complete iff for every well–signed atom of the form p(s, t) there exists a clause p(l, r) ← B1 ∧ ! ∧ B2 such that hl = s ∧ B1 , idi →∗P htrue, ηi holds for a suitable substitution η. If a program P is complete, then it is easy to see that the normal form of a well–signed frame F has either the form htrue, ϑi or hfalse, ϑi for a suitable substitution ϑ. We define a function nf (F ) that returns true in the first case and false in the second, i.e. true if F →∗P htrue, i; nf (F ) = false otherwise. For the rest of this paper we make the assumption that all programs are universally terminating, complete, and confluent. It is not too hard to see that the computed answer of a well–signed query G can be computed by computing the normal form of the frame hG, idi. If this normal form is htrue, ϑi, then the computed answer is ϑ|var(G) , where ϑ|var(G) denotes the restriction of the substitution ϑ to the variables of G. If the normal form is hfalse, ϑi, then the Prolog resolution of G fails. In order to show the declarative semantics defined later to be consistent, we need a Lifting Lemma for the relation →∗P . To formulate this lemma we introduce the notion of a substitution being more general than another substitution. Formally we define ϑ to be more general than ϕ iff there exists a substitution η such that ϑη = ϕ. If ϑ is more general than ϕ, then this is written as ϕ ≤ ϑ. Lemma 6 (Lifting Lemma) Assume that the frames hB1 , σ1 i and hB1 , ϑ1 i are well–signed and that, furthermore, the following holds: 1. hB1 , σ1 i →∗P hB2 , σ1 σ2 i. 2. σ1 ≤ ϑ1 , i.e. ϑ1 is more general than σ1 . Then there exists a substitution ϑ2 such that the following holds: 1. hB1 , ϑ1 i →∗P hB2 , ϑ1 ϑ2 i. 2. σ1 σ2 |var(B1 ) ≤ ϑ1 ϑ2 |var(B2 ) .
8
A proof of this lemma can be given by a straightforward induction along the definition of the relation →P .
4
A Declarative Semantics for Functional Programs
In this section we define the declarative semantics for functional programs that are well-signed. Assume a clause of the form p(s, t) ← B1 ∧ ! ∧ B2 is given. In order to give the declarative semantics of this clause we introduce a sequence of new variables y that has the same length as the sequence t. Furthermore, assume that z is the tuple of all those variables that occur in t or in B2 , but do not occur in s nor in B1 , i.e. {z} = var(t) ∪ var(B2 ) \ var(s) ∪ var(B1 ) . Then the declarative semantics of the clause given above is defined to be the formula B1 → p(s, y) ↔ ∃z.B2 ∧ t = y . Of course, t = y is short for t1 = y1 ∧ · · · ∧ tn = yn . The declarative semantics of a program P is then defined to be the collection of the declarative semantics of all of its clauses together with the axioms of CET (Clark’s equational theory) [Cla78]. It will be denoted as decl(P). Note that if the declarative semantics decl(P) of a program P is defined in this way then the semantics is local in the sense that every single clause is translated into a single formula. This is in contrast to the completion semantics comp(P) since there a definition, i.e. the set of all clauses defining a predicate, is translated into a formula. Therefore the completion semantics is considerably more complex than our semantics. Another important difference between our approach and the completion semantics is the fact that we interpret decl(P) as a set of typed formulas, whereas comp(P) is regarded as a set of plain, i.e. untyped, first order formulas. To proceed, we consider the predicate remove/3 as an example. The call remove( L, X, S ) removes the first occurrence of X in the list L. The result is the list S. The predicate fails if X is not a member of L. The signature and implementation of this predicate are given as follows: %# signature: remove( +list(T), +T, -list(T) ). remove( [ X | Xs ], Y, Xs ) :X = Y, !. remove( [ X | Xs ], Y, [ X | Rs ] ) :9
not X = Y, !, remove( Xs, Y, Rs ). remove( [], Y, Rs ) :!, fail. Note that the last clause is necessary in order for the implementation to be complete in the sense defined in the last section. The declarative semantics of these clauses is given by the following formulas: X = Y → remove([X|Xs], Y, Zs) ↔ true ∧ Xs = Zs . X 6= Y → remove([X|Xs], Y, Zs) ↔ ∃Rs.remove(Xs, Y, Rs) ∧ [X|Rs] = Zs . true → remove([], Y, Zs) ↔ ∃Rs.false ∧ Rs = Zs . It should be obvious that these formulas are much more handy than the corresponding formulas resulting from the completion semantics.
4.1
Constructing a Model of decl(P)
As yet, we have not shown the declarative semantics to be consistent. We will do this now by constructing a model P of decl(P). P will be defined as a Herbrand interpretation [Apt90], i.e. the domain of P is the set of all well–typed ground terms and the interpretation fP of an n–ary function symbol f is defined via fP (t1 , · · · , tn ) = f (t1 , · · · , tn ). Furthermore, if p is an n–ary predicate symbol, then this predicate symbol is interpreted as the set of tuples of ground terms t1 , · · · , tn such that hp(t1 , · · · , tn ), idi →∗P htrue, i, i.e. we define pP = ht1 , · · · , tn i : nf (hp(t1 , · · · , tn ), idi) = true . The fact that decl(P) is consistent follows from the following theorem. Theorem 7 If P is universally terminating, confluent, and complete, then P |= decl(P). Proof (sketch): It follows from [Cla78] that P is a model of CET. Furthermore, we have to show that P |= B1 → p(l, y) ↔ ∃z.B2 ∧ y = r (1) holds for all clauses p(l, r) ← B1 ∧ ! ∧ B2 in P, where z is the tuple of variables that are local to r and B2 . In order to show (1) we assume that a ground substitution σ is given such that (a) B1 → p(l, y) ↔ ∃z.B2 ∧ y = r σ is a formula containing no free variables, and 10
(b) P |= B1 σ holds true. Now from P |= B1 σ and the definition of P it follows that nf (hB1 σ, idi) = true. Since B1 σ is ground this implies hB1 , σi →∗P htrue, τ i, where τ ≤ σ and τ |var(B1 ) = σ|var(B1 ) . To proceed, we show that hl = lσ ∧ B1 , idi →∗P htrue, %i for a suitable substitution %. The definition of →P shows hl = lσ ∧ B1 , idi →∗P hB1 , µi with µ = mgu(l, lσ). Obviously, σ ≤ µ. Therefore (2) and the Lifting Lemma show that there substitution % such that σ|var(B1 ) ≤ %|var(B1 ) and hB1 , µi →∗P htrue, %i, (4) and (5) yield (3). Using (3) it follows that hp(lσ, yσ), idi →P hB2 % ∧ yσ = r%, idi Therefore, we have nf hp(lσ, yσ), idi = nf hB2 % ∧ yσ = r%, idi . There are two cases. 1. Case: nf hp(lσ, yσ), idi = true. In this case there is a substitution γ such that hB2 % ∧ yσ = r%, idi →∗P htrue, γi. Then it is easy to see that nf hB2 %γ ∧ yσ = r%γ, idi = true. The definition of P shows therefore that (1) is valid. 2. Case: nf hp(lσ, yσ), idi = false. We have to show P 6|= ∃z.B2 σ ∧ yσ = rσ. Let us assume the contrary. Then there is a substitution η such that nf hB2 ση ∧ yσ = rση, idi = true The Lifting Lemma shows then nf hB2 % ∧ yσ = r%, idi = true which, because of our assumption for this case, contradicts (6).
5
(2) (3) (4) is a (5)
(6)
2
Soundness and Completeness
In this section we show the semantics decl(P) to be correct and complete for programs that are universally terminating, confluent, and complete. 11
In the following we need to be able to interpret substitutions as formulas. If ϑ = {x1 /t1 , · · · , xn /tn } is a substitution, then ϑ is interpreted as the formula x1 = t1 ∧ · · · ∧ xn = tn . The identity substitution id is interpreted as true. The key lemma is the following: Lemma 8 Assume hG1 , ϑ1 i is a well–signed frame. Then, if hG1 , ϑ1 i →P hG2 , ϑ1 ϑ2 i holds, then the following equivalence is valid: decl(P) |= G1 ϑ1 ↔ ∃z.G2 ϑ1 ϑ2 ∧ ϑ2 , where z is the tuple of all those variables occurring in the formula G2 ϑ1 ϑ2 ∧ ϑ2 but not in G1 ϑ1 , i.e. {z} = var(G2 ϑ1 ϑ2 ∧ ϑ2 )\var(G1 ϑ1 ). Proof: The claim is shown by induction following the definition of the rewrite relation →P . There are eight cases. 1. hG1 , ϑ1 i = hp(s, t), ϑi, P contains a clause p(l, r) ← B1 ∧ ! ∧ B2 , we have hl = sϑ ∧ B1 , idi →∗P htrue, ηi, (1) and therefore we have hG2 , ϑ1 ϑ2 i = hB2 η ∧ t = rη, ϑi, i.e. ϑ1 = ϑ and ϑ2 = id. By induction hypothesis (and a trivial side induction along the definition of the transitive closure →∗P of →P ), (1) yields decl(P) |= l = sϑ ∧ B1 ↔ ∃u.true ∧ η, where {u} = var(η)\var(l = sϑ ∧ B1 ). We instantiate this formula with η and use the fact that, since η is ground, we have |= ηη ↔ true. 2 Since sϑ is ground we get decl(P) |= lη = sϑ ∧ B1 η. (2) Furthermore, decl(P) contains the formula B1 → p(l, y) ↔ ∃v.B2 ∧ r = y , where {v} = var(B2 ∧ r = y)\var(B1 → p(l, y)). Instantiating this formula with η and using the fact that y is a tuple of variables not occurring elsewhere we get decl(P) |= B1 η → p(lη, y) ↔ ∃v.B2 η ∧ rη = y . Because of (2) this shows 2 Remember that, if η is the substitution {x /s , · · · , x /s }, then the first occurrence of η n n 1 1 in the expression ηη is interpreted as the formula x1 = s1 ∧ · · · · · · xn = sn , while the second η is interpreted as the substitution applied to this formula. Since η is ground we have si η = si for all i = 1, · · · , n. Furthermore, by the definition of η we have xi η = si Therefore ηη is the formula s1 = s1 ∧ · · · ∧ sn = sn .
12
decl(P) |= p(sϑ, y) ↔ ∃v.B2 η ∧ rη = y. Instantiating y with tϑ we see decl(P) |= p(s, t)ϑ ↔ ∃v.B2 η ∧ rη = tϑ. However, this is the claim that had to be proved. 2. hG1 , ϑ1 i = hs = t, ϑi, µ = mgu(sϑ, tϑ) is a most general unifier of sϑ and tϑ that is both idempotent and relevant, and hG2 , ϑ1 ϑ2 i = htrue, ϑµi. We have to show decl(P) |= sϑ = tϑ ↔ true ∧ µ. However, this is an easy consequence of Clark’s equational theory [Cla78]. 3. hG1 , ϑ1 i = hs = t, ϑi, the terms sϑ and tϑ are not unifiable, and, furthermore, we have hG2 , ϑ1 ϑ2 i = hfalse, ϑi. We then have to show decl(P) |= sϑ = tϑ ↔ false. Again, this is an easy consequence of Clark’s equational theory. 4. hG1 , ϑ1 i = hnot Q, ϑi, we have hQ, ϑi →∗P hfalse, ϑηi, and, furthermore, hG2 , ϑ1 ϑ2 i = htrue, ϑi. The induction hypothesis yields then decl(P) |= Qϑ ↔ ∃v.false ∧ η, for a suitable v. This is equivalent to decl(P) |= ¬Qϑ ↔ true. and this is the claim that had to be proved. 5. hG1 , ϑ1 i = hnot Q, ϑi, we have hQ, ϑi →∗P htrue, ϑηi, and, furthermore, hG2 , ϑ1 ϑ2 i = hfalse, ϑi. The induction hypothesis yields decl(P) |= Qϑ ↔ ∃v.true ∧ η for a suitable v. Instantiating this formula with η and using the fact that, due to the well–signedness of the query not Qϑ, we must have Qϑη = Qϑ, we can conclude decl(P) |= Qϑ. This is the same as decl(P) |= ¬Qϑ ↔ false, which is the claim that had to be proved. 6. hG1 , ϑ1 i = htrue ∧ G, ϑi and hG2 , ϑ1 ϑ2 i = hG, ϑi. In this case the claim is trivial. 7. hG1 , ϑ1 i = hfalse ∧ G, ϑi and hG2 , ϑ1 ϑ2 i = hfalse, ϑi. In this case the claim is trivial.
13
8. hG1 , ϑ1 i = hL ∧ B2 , ϑi, where L is a literal such that hL, ϑi →∗P hB1 , ϑηi. (3) Since hG2 , ϑ2 i = hB1 ∧ B2 , ϑηi we have to show decl(P) |= (L ∧ B2 )ϑ ↔ ∃v.(B1 ∧ B2 )ϑη ∧ η, (4) where {v} = var((B1 ∧ B2 )ϑη ∧ η)\var((L ∧ B2 )ϑ). Because of (3) the induction hypothesis yields decl(P) |= Lϑ ↔ ∃u.B1 ϑη ∧ η, (5) where {u} = var(B1 ϑη ∧η)\var(Lϑ). It can be shown that u = v. Therefore, (4) is a consequence of (5) and the fact that for arbitrary formulas A and substitutions ϕ |= ϕ → (A ↔ Aϕ) is valid. 2 The soundness and completeness of Prolog resolution are simple consequences of lemma 8. Theorem 9 (Soundness) Assume that P is a program that is universally terminating, complete, confluent, and well–signed, and G is a well–signed query. Then we have the following: 1. If ϑ is a computed answer for G, then decl(P) |= Gϑ. 2. If the Prolog resolution of G fails, then decl(P) |= ¬G. Proof: We prove both claims separately. 1. Since ϑ is a computed answer for G we must have hG, idi →∗P htrue, ϑi. Repeated application of Lemma 8 shows decl(P) |= G ↔ ∃v.true ∧ ϑ, where {v} = var(ϑ)\var(G). From this the claim follows by instantiating with ϑ and using the fact that, since ϑ is ground, we have |= ϑϑ. 2. In this case we have hG, idi →∗P hfalse, ηi for a suitable substitution η. This time Lemma 8 shows decl(P) |= G ↔ ∃v.false ∧ η, where {v} = var(η)\var(G). From this the claim is immediate.
2
Theorem 10 (Completeness) Assume that P is a program that is universally terminating, complete, confluent, and well–signed, and G is a well–signed query. Then we have the following:
14
1. If decl(P) |= Gϕ and Gϕ is ground, then ϕ|var(G) is a computed answer for G. 2. If decl(P) |= ¬G, then the Prolog resolution of G fails. Proof: We prove both claims separately. 1. The normal form of hG, idi is either of the form hfalse, ηi or htrue, ϑi. In the first case Lemma 8 shows decl(P) |= ¬G which, due to the fact that decl(P) is consistent, contradicts the assumption decl(P) |= Gϕ. Therefore we must have hG, idi →∗P htrue, ϑi for a suitable substitution ϑ. Lemma 8 shows decl(P) |= G ↔ ∃v.ϑ, where {v} = var(ϑ)\var(G). Instantiating with ϕ we see decl(P) |= Gϕ ↔ ∃v.ϑϕ. Our assumption decl(P) |= Gϕ therefore shows decl(P) |= ∃v.ϑϕ. Assume that ϑ = {x1 /s1 , · · · xn /sn }. Since the si are ground we therefore have decl(P) |= ∃v.x1 ϕ = s1 ∧ · · · ∧ xn ϕ = sn . We know that xi ϕ is ground for xi ∈ var(G). Therefore, Clark’s equational theory shows that xi ϕ = si for all xi such that xi ∈ var(G). Hence our claim is proved in this case. 2. Assume that the Prolog resolution of G does not fail. Then there would be a computed answer ϑ and the Soundness Theorem would show decl(P) |= Gϑ. This would contradict decl(P) |= ¬G. 2
6
Applications
Providing a declarative semantics for Prolog is of more than a mere theoretical interest. It offers the possibility of applying formal methods to Prolog programs. For instance, St¨ ark has shown how to verify pure Prolog programs [St¨ a95, St¨ a96]. Using the declarative semantics developed in this paper it is possible to verify even programs using the cut operator. However, at the present stage a complete verification of programs is very costly. Therefore, except for certain safety–critical systems, verification is economically not viable. Nevertheless, there are a number of properties of programs that can be verified more or less automatically. One of these properties is completeness. It can be shown that the property of provable completeness defined below entails completeness. Definition 11 (Provable Complete) If p is a predicate with signature p(+σ, −τ ) 15
and if p is implemented by n clauses of the form p(li , r i ) ← Bi ∧ ! ∧ Ci , i = 1, · · · , n, then p is provable complete iff for every tuple s of ground terms satisfying s ∈ σ we are able to show n W ∃v i .li = s ∧ Bi , (∗) decl(P) |= i=1
where {v i } = var(li = s ∧ Bi ). Experience has shown that (∗) is a verification condition that can be proven automatically, even if the implicit quantification over all s ∈ σ is taken into account. Therefore, automatic theorem provers can be used to spot predicates whose implementations are incomplete. We have implemented a tool called Pan that checks, among other properties, the completeness of a program [SG95]. So far, our experience has shown that checking the completeness of a program can uncover subtle errors which might otherwise go undetected. This corresponds to similar experiences that have been observed with the Mercury 3 system [SHC95]. Mercury is a modern, purely declarative successor of Prolog. The Mercury compiler offers a determinism analysis of programs. This analysis corresponds roughly to our checking of completeness. The Mercury team has reported [HSC96] that the determinism analysis facilitates the maintenance of programs greatly. We have made similar experiences using the completeness analysis implemented in our tool. Besides checking completeness, our tool is able to verify the property of a program being universally terminating. The details are presented in [M¨ ul95]. Up to now declarative languages have not made a significant impact on software industry. Our opinion is that this situation can be changed by exploiting the fact that the application of formal methods is much easier for a language possessing a clean, declarative semantics than it is for an imperative language. Recent advances (in particular, we would like to mention here the work of Bouhoula et. al. [Bou94, BR95], and Bachmair and Ganzinger [BG94]) in the field of automatic theorem proving should be exploited and transferred to the field of static analysis of declarative languages. Our own experiences with the use of theorem provers in software verification [Str95] have shown that the time has come for a closer integration of these fields. This will result in both a higher productivity and a higher quality in the use of declarative languages. We view our work as a step in this direction. Acknowledgments. The authors would like to thank Andrea Engelhardt, Fergus J. Henderson, and Prof. Helmut Schwichtenberg for several helpful discussions on the topic of this paper. Furthermore, thanks go to Martin M¨ uller for designing and implementing that part of our Pan that is responsible for checking the universal termination of predicates. 3 Mercury
is available at http://www.cs.mu.oz.au/~zs/mercury.html.
16
References [AE93]
Krzysztof R. Apt and Sandro Etalle. On the unification free Prolog programs. In S. Sokolowski, editor, Proceedings of the Conference on Mathematical Foundations of Computer Science (MFCS ‘93), Lecture Notes in Computer Science. Springer Verlag, 1993. Available at: http://www.cwi.nl/cwi/publications/index.html.
[And95]
James Andrews. The logical semantics of the prolog cut. In John W. Lloyd, editor, International Logic Programming Symposium ‘95. MIT, 1995.
[Apt90]
Krzysztof R. Apt. Logic programming. In J. van Leeuwen, editor, Handbook of Theoretical Computer Science, Volume B, chapter 10, pages 495–574. Elsevier, 1990.
[Apt92]
Krzysztof R. Apt, editor. Logic Programming. The MIT Press, 1992.
[Apt93]
Krzysztof R. Apt. Declarative programming in Prolog. In D. Miller, editor, Proceedings of International Logic Programming Symposium (ILPS ‘93), Cambridge, Mass./London, 1993. The MIT Press. Available at: http://www.cwi.nl/cwi/publications/index.html.
[BG94]
L. Bachmair and H. Ganzinger. Rewrite-based equational theorem proving with selection and simplification. Journal of Logic and Computation, 4(3):217–247, 1994.
[BLR92]
Fran¸cois Bronsard, T. K. Lakshman, and Uday S. Reddy. A framework of directionality for proving termination of logic programs. In Apt [Apt92], pages 321–335.
[B¨ or89]
E. B¨ orger. A logical operational semantics of full Prolog, part I: Selection core and control. In E. B¨orger, H. Kleine B¨ uning, and M. M. Richter, editors, Computer Science Logic CSL ’89, pages 36– 64. Springer-Verlag, Lecture Notes in Computer Science 440, 1989.
[Bou94]
Adel Bouhoula. SPIKE: a system for sufficient completeness and parameterized inductive proofs. In Alan Bundy, editor, Twelfth International Conference on Automated Deduction, LNAI 814, pages 836–840, Nancy, France, 1994. Springer-Verlag.
[BR95]
Adel Bouhoula and Micha¨el Rusinowitch. Implicit induction in conditional theories. Journal of Automated Reasoning, 1995.
[Cla78]
K. L. Clark. Negation as failure. In H. Gallaire and J. Minker, editors, Logic and Data Bases, pages 293–322. Plenum Press, New York, 1978.
17
[dBdV89] A. de Bruin and E.P. de Vink. Continuation semantics for Prolog with cut. In Theory and practice of software engineering, volume 351 of Lecture Notes in Computer Science, pages 178–192. Springer-Verlag, 1989. [DM85]
P. Dembinski and J. Maluszynski. And–parallelism with intelligent backtracking for annotated logic programs. In Proceedings of the International Symposium on Logic Programming, 1985.
[HSC96]
Fergus Henderson, Zoltan Somogyi, and Thomas Conway. Determinism analysis in the mercury compiler. In Proceedings of the Australian Computer Science Conference, 1996. Available at: http://www.cs.mu.oz.au/~zs/mercury/papers.html.
[JM84]
N. D. Jones and A. Mycroft. Stepwise development of operational and denotational semantics for prolog. In 1984 International Symposium on Logic Programming, pages 281–288. IEEE, 1984.
[Llo87]
J. W. Lloyd. Foundations of Logic Programming. Springer–Verlag, Berlin, second edition, 1987.
[LM87]
J.-L. Lassez and M. J. Maher. Unification revisited. In J. Minker, editor, Foundations of Deductive Databases and Logic Programming, pages 587–625. Morgan Kaufmann, Los Altos, 1987.
[MO84]
Alan Mycroft and Richard A. O’Keefe. A polymorphic type system for Prolog. Artificial Intelligence, 23:295–307, 1984.
[M¨ ul95]
Martin M¨ uller. Automatisierbare Terminierungsnachweise von Prolog–Programmen. Master’s thesis, Universit¨at Marburg, 1995. Available at: http://www.mathematik.uni-marburg.de/~mueller/termnach.ps.
[PS95]
Andreas Podelski and Gert Smolka. Operational semantics of constraint logic programs with coroutining. In Leon Sterling, editor, Proceedings of the 1995 International Conference on Logic Programming, pages 449–463, Kanagawa, Japan, 13–18 June 1995. The MIT Press.
[SG95]
Karl Stroetmann and Thomas Glaß. Augmented prolog — An evolutionary approach. In Donald A. Smith, Olivier Ridoux, and Peter Van Roy, editors, Proceedings of the Workshop Visions for the Future of Logic Programming: Laying the Foundations for a Modern Successor to Prolog, pages 59– 70, 1995. The Proceedings of this workshop are available at: ftp://ps-ftp.dfki.uni-sb.de/pub/ILPS95-FutureLP/.
18
[SHC95]
Zoltan Somogyi, Fergus Henderson, and Thomas Conway. Logic programming for the real world. In Donald A. Smith, Olivier Ridoux, and Peter Van Roy, editors, Proceedings of the Workshop Visions for the Future of Logic Programming: Laying the Foundations for a Modern Successor to Prolog, pages 83– 94, 1995. The Proceedings of this workshop are available at: ftp://ps-ftp.dfki.uni-sb.de/pub/ILPS95-FutureLP/.
[St¨ a95]
Robert F. St¨ ark. Formal methods for logic programming systems. Technical report, Department of Mathematics, Stanford University, 1995. Available at: http://www-leland.stanford.edu/~staerk/list.html.
[St¨ a96]
Robert F. St¨ ark. Total correctness of pure Prolog programs: A formal approach. This volume.
[Str95]
Karl Stroetmann. Seduct — a proof compiler for first order logic. In Manfred Broy and Stefan J¨anichen, editors, KORSO: Methods, Languages, and Tools for the Construction of Correct Software, volume 1009 of Lecture Notes in Computer Science. Springer Verlag, 1995.
[Vod88]
Paul J. Voda. The logical reconstruction of cuts as one solution operators. In John W. Lloyd, editor, Proceedings of the Workshop on Meta-programming in Logic Programming, pages 379–384. 1988.
19