TLP 3 (2): 189–221, March 2003.
c 2003 Cambridge University Press
DOI: 10.1017/S1471068402001527
Printed in the United Kingdom
189
Composing programs in a rewriting logic for
declarative programming
J. M. MOLINA-BRAVO and E. PIMENTEL
Dpto. Lenguajes y Ciencias de la Computación, University of Málaga,
Campus de Teatinos, 29071 Málaga, Spain
(e-mail: {)jmmb, ernesto}@lcc.uma.es)
Abstract
Constructor-Based Conditional Rewriting Logic is a general framework for integrating
first-order functional and logic programming which gives an algebraic semantics for nondeterministic functional-logic programs. In the context of this formalism, we introduce a
simple notion of program module as an open program which can be extended together
with several mechanisms to combine them. These mechanisms are based on a reduced set of
operations. However, the high expressiveness of these operations enable us to model typical
constructs for program modularization like hiding, export/import, genericity/instantiation,
and inheritance in a simple way. We also deal with the semantic aspects of the proposal by
introducing an immediate consequence operator, and studying several alternative semantics
for a program module, based on this operator, in the line of logic programming: the operator
itself, its least fixpoint (the least model of the module), the set of its pre-fixpoints (term
models of the module), and some other variations in order to find a compositional and fully
abstract semantics w.r.t. the set of operations and a natural notion of observability.
KEYWORDS: functional-logic programming, modules, compositionality, full abstraction, semantics
1 Introduction
Constructor-Based Conditional Rewriting Logic (CRWL)1 , presented in GonzálezMoreno et al. (1999), is a quite general approach to declarative programming that
combines (first-order) functional and logic paradigms by means of the notion of
(possibly) non deterministic lazy function. The basic idea is that both relations and
deterministic lazy functions are particular cases of non-deterministic lazy functions. This approach retains the advantages of deterministic functions while adding
the possibility of modeling non-deterministic functions by means of non-confluent
constructor-based term rewriting systems, where a given term may be rewritten to
constructor terms (possibly with variables) in more than one way. Here a fundamental notion is that of joinability: two terms a,b are joinable iff they can be rewritten
to a common – but not necessarily unique – constructor term. In González-Moreno
1
CRWL must not be confused with the Rewriting Logic proposed in Meseguer (1992) as a unifying
logical framework for concurrency. CRWL is a particular logic for dealing with indeterminism.
190
J. M. Molina-Bravo and E. Pimentel
et al. (1999), CRWL is introduced with two equivalent proof calculi that govern
deduction in this logic, an algebraic semantics for programs (theories) based on a
freely generated model, and an operational semantics, based on a lazy narrowing
calculus for solving goals, that is sound and complete w.r.t. the algebraic semantics.
Modularity is a central issue in all programming paradigms motivated by the
need of mastering the complexity inherent in large programs. Modularity related
with algebraic specifications (which, to some extent, can be viewed as a sort of firstorder functional programming) has been extensively studied and all specification
languages are extended for dealing with modules. In this field, a typical module
consists of a body, an export interface, a list of imports and, possibly, a list of
formal parameters, and typical operations with modules have to do with setting
up hierarchical relationships between modules as the union of modules (with some
constraints) and the application of a parameterized module to an actual module,
and their semantics are given from a category-theoretic point of view (Goguen
and Burstall, 1992; Ehrig and Mahr, 1990; Orejas, 1999). Nevertheless, there are
other studies of modularity (Wirsing, 1990) with more flexible sets of operations
semantically defined by means of operations on the sets of models, and also studies
where modularity has been tackled with the tools of algebraic specifications, as in
Bergstra et al. (1990), where an axiomatic specification is given for an algebra of
non-parameterized modules and it is proved that each expression can be reduced to
another one with, at most, an occurrence of the export (hiding) operator, and Durán
(1999) where a constructive specification is given for an algebra of parameterized
modules (without hiding) in Maude, and each expression is reduced to a flat module.
In the logic programming field, modularity has been the objective of different
proposals – see Bugliesi et al. (1994) for a survey about the subject – which basically
have followed two different guidelines. One, focused on programming-in-the-large,
extends logic programming with modular constructs as a meta-linguistic mechanism
(Brogi and Turini, 1995) and gives semantics to modules with the aid of the
immediate consequence operator. And the other one, focused on programmingin-the-small, enriches the theory of Horn clauses with new logical connectives for
dealing with modules (Miller, 1986). In the first line, there is the work Brogi (1993)
where an algebra of logic programs is studied. This algebra is based on three basic
operations (union, intersection and encapsulation) defined at the semantic level and
then translate to the syntactic level. It is proved that each program expression is
equivalent to a, possibly infinite, flat program, and also a transformation is defined for
mapping program expressions into finite programs by introducing system generated
predicates and adding a hidden part to each program. Notions of module hiding
some predicates and module importation are built up with the aid of the basic
operations.
On the other hand, in functional-logic programming we do not know any study of
modularity semantically well founded. With this paper we have tried to contribute
to filling this gap at least in the CRWL context. In this context, we deal with
data constructors, as in logic programming, and functions defined by conditional
rewrite rules, instead of predicates defined by Horn clauses, and we have proved (see
section 3) that an operator, similar to the immediate consequence operator of logic
Composing programs in CRWL
191
programs, can be defined to each CRWL-program and its least fixpoint coincides
with the freely generated term-model given in González-Moreno et al. (1999). All
this has motivated our decision of developing a study of programs structuring and
modularity in CRWL, based on a meta-linguistic mechanism, similar to the one
which appears in Brogi (1993). However, we have defined an algebra of program
modules based on a different set of operations (union, deletion of a signature of
function symbols, closure w.r.t. a signature and renaming) defined at the syntactical level in such a way that each program expression can be reduced to a, possibly
infinite, flat program. With these operations we can model as well as notions of module which hides some functions and module importation, module parameterization,
instantiation and inheritance with overriding. Also, we have introduced a notion
of protected signature labeling symbols with module expressions, which allows to
define structured modules and a representation morphism that maps each program
expression into a finite structured module. We use protected signature, not only for
hiding functions as is done in Brogi (1993) for predicates, but also for hiding data
constructors.
An important aspect to be considered when a language is extended for modular
programming is the sound integration of the behavior of the modular operations
into the semantics of the language. The compositionality of the semantics of a
programming language is particularly relevant when modularity is involved. In fact,
one of the most critical aspects in modular systems is the possibility of making a
separate compilation of modules, and this can only be made in the presence of this
property. On the other hand, full abstraction measures the implementation details of
the semantics of a programming language. A non-fully abstract semantics makes the
intended meaning of a program to include non relevant aspects, which do not depend
on the behavior of the program but on a particular ‘implementation’. In some sense,
full abstraction can be seen as the complementary property of compositionality,
and the adequacy of a semantics is established when both full abstraction and
compositionality are obtained. In Brogi (1993), the semantics of a program is given
by its immediate consequence operator which captures the information concerning
possible compositions, this semantics is compositional by construction and it is
proved that also is fully abstract w.r.t. a notion of observable behavior given by the
success sets of programs (least fixpoints of their immediate consequence operators).
In CRWL-programming, the semantics given by the immediate consequence operator
is compositional but not fully abstract when we take the freely generated term-model
as observable behavior. For this reason, we study several alternative semantics to
find one that is compositional and fully abstract.
We are confident that our work could serve as a reference to other studies
of modularity in functional-logic programming, and, although we are focused on
the modular aspects of the semantics, the results obtained in this paper, as well
as the study of a wide range of other issues concerning semantics, makes the
current work also relevant from a purely semantic point of view, in the context
of rewriting logic-based programming languages. The approach to modularity in
CRWL-programming, that we present here, substantially extends a previous one
in Molina-Bravo and Pimentel (1997) with a more elaborate notion of program
192
J. M. Molina-Bravo and E. Pimentel
module and a new operation (renaming) that makes clear the difference between
importation and instantiation, and a more recent one (Molina-Bravo, 2000) with
the notions of structured module and module representation that allows to express
closed modules by means of a finite number of rules and also to deal with local
constructor symbols. For space reasons we do not include the proofs of the results;
they are available at http://arxiv.crg/abs/cs/0203006.
The paper is organized as follows: in the next section we introduce the basic
features of the CRWL approach to functional-logic programming and its modeltheoretic semantics – for a detailed presentation we refer to González-Moreno et
al. (1999). In section 3 we introduce an immediate consequence operator TR , for
each CRWL-program R, and a fixpoint semantics that matches the free term-model
MR proposed in González-Moreno et al. (1999). In section 4 we define a notion
of (plain) module together with a reduced set of operations on program modules,
and we express some modular constructions with these operations. In section 5 we
give the T-semantics that characterizes the meaning of a CRWL-program when we
consider composition of programs and prove that this semantics is compositional but
not fully abstract w.r.t. the set of operations, taking MR as the observable behavior
of a program R. In section 6 we introduce a fully abstract semantics by denoting a
program module with the set of all its consistent term-models (pre-fixpoints of TR );
but this semantics is not compositional for the deletion of a signature. In section 7,
we obtain a compositional and fully abstract semantics as an indexed family of sets
of consistent term-models for single functions. In section 8, we introduce the notion
of structured module as a finite representation of expressions made up from finite
plain modules that allows the hiding of constructor symbols. Finally we present a
discussion and some conclusions.
2 CRWL for declarative programming
A signature with constructors is a pair Σ = (DSΣ , FSΣ ), where DSΣ and FSΣ are
countable disjoint sets of strings h/n with n ∈ N. Each c such that c/n ∈ DSΣ is
a constructor symbol with arity n and each f such that f/n ∈ FSΣ is a (defined)
function symbol with arity n. The set of all constructor symbols and the set of all
function symbols with arity n are denoted by DSΣn and FSΣn , respectively. Given a
signature (with constructors) Σ and a set V of variable symbols, disjoint from all
of the sets DSΣn and FSΣn , we define Σ-terms as follows: each symbol in V and each
symbol in DSΣ0 ∪ FSΣ0 is a Σ-term, and for each h ∈ DSΣn ∪ FSΣn and t1 , . . . , tn terms,
h(t1 , . . . , tn ) is a term. TermΣ is the set of all Σ-terms and CTermΣ the subset of those
Σ-terms (called constructor terms) built up only with symbols in DSΣ and V. To
cope with partial definition we add a new 0-arity constructor ⊥ to each signature Σ
obtaining an extended signature Σ⊥ whose terms are called partial Σ-terms. When the
signature Σ is clear, we will omit explicit mention of it, and we will write Term and
CTerm (or Term⊥ and CTerm⊥ for Σ⊥ ) respectively. Following the approach to nondeterminism in Hussmann (1993), we only consider C-substitutions θ: V → CTerm.
These mappings have natural extensions θ: Term → Term, also noted as θ, defined in
the usual way, and the result of applying θ to the term t is written tθ. Analogously,
Composing programs in CRWL
193
we define partial C-substitutions as mappings θ: V → CTerm⊥ . The set of all
C-substitutions (partial C-substitutions) is written CSubst (CSubst⊥ ). A signature
morphism ρ: Σ → Σ′ from a signature Σ = (DSΣ , FSΣ ) to a signature Σ′ = (DSΣ′ , FSΣ′ )
consists of two mappings, that we denote with the same symbol ρ: DSΣ → DSΣ′ and
ρ: FSΣ → FSΣ′ , that map strings h/n into strings h′ /n. By abuse of notation we
will denote h′ = ρ(h). This allows us to define a mapping ρ: TermΣ⊥ → TermΣ′ ⊥ as
ρ(h) =def h, for h ∈ V ∪ {⊥} ∪ DSΣ0 ∪ FSΣ0 ; ρ(h(t)) =def ρ(h)(ρ(t1 ), . . . , ρ(tn )), for h ∈
DSΣn ∪ FSΣn , n > 0. ρ(h) =def h, for h ∈ V ∪ {⊥} ∪ DSΣ0 ∪ FSΣ0 , and ρ(h(t)) =def
ρ(h)ρ(t1 ), . . . , ρ(tn )), for h ∈ DSΣn ∪ FSΣn and n > 0. We will consider signature
morphisms ρ: Σ → Σ such that ρ(h/n) = h/n for every string h/n in DSΣ . Such
morphisms will be called function symbol renamings.
Given a signature Σ and a set V of variable symbols, there are two kinds of atomic
CRWL-formulas for a, b ∈ Term⊥ , reduction statements a → b, with the intended
meaning “a can be reduced to b,” and joinability statements a ⊲⊳ b, with the intended
meaning “a and b can be reduced to a common value in CTerm”. Terms t ∈ CTerm
are intended to represent totally defined values whereas terms t ∈ CTerm⊥ represent
partially defined values — to model the behavior of non-strict functions. Reduction
statements a → t with t ∈ CTerm⊥ , called approximation statements, have the
intended meaning that t approximates a possible value of a, whereas a → t with
t ∈ CTerm have the intended meaning that t represents a possible value of a – an
expression may denote several values capturing the behavior of non-deterministic
functions. Substitutions θ ∈ CSubst⊥ and signature morphisms ρ: Σ → Σ′ apply to
formulas in the obvious way.
A CRWL-program is a CRWL-theory R defined as a signature Σ together with
a set of conditional rewrite rules of the general form f(t) → r ⇐ C, where f(t) is
the left-hand side (lhs), r the right-hand side (rhs), C the condition of the rule, f
is a function symbol with arity n > 0, and C consists of finitely many (possibly
zero) joinability statements between fully defined terms (with no occurrence of ⊥).
When n > 0, t is a linear n-tuple (i.e. without repeated variables) of fully defined
constructor terms ti ∈ CTerm. When n = 0 rules take the simpler form f → r ⇐ C.
Formal derivation of CRWL-statements from a given program R is governed by
two equivalent calculi (see González-Moreno et al. (1999)). We present here the
so-called Goal-Oriented Proof Calculus (GPC), which focuses on top-down proofs
of reduction and joinability statements:
(Bo)
e → ⊥,
for e ∈ Term⊥ ;
(RR)
e → e,
for e ∈ V ∪ DS 0 ;
(DS)
e1 → t1 . . . en → tn
,
c(e) → c(t)
(OR)
(Jo)
e1 → t1 . . . en → tn C r → t
,
f(e) → t
a→t b→t
,
a ⊲⊳ b
for c ∈ DS n and ei , ti ∈ Term⊥ ;
if (f(t) → r ⇐ C) ∈ [R]⊥ and t 6≡ ⊥;
if t ∈ CTerm and a, b ∈ Term⊥ ;
where [R]⊥ = {(l → r ⇐ C)θ | (l → r ⇐ C) ∈ R, θ ∈ CSubst⊥ } is the set of possibly
partial constructor instances of rewrite rules and C-substitutions apply to rules in
194
J. M. Molina-Bravo and E. Pimentel
the obvious way. Rule (Bo) shows that a CRWL-reduction is related to the idea of
approximation, and rule (OR) states that only constructor instances of rewrite rules
are allowed in this calculus reflecting the so-called ‘call-time-choice’ (Hussmann,
1993) for non-determinism (values of arguments for functions are chosen before
the call is made). When a reduction or joinability statement ϕ is derivable from
a program R we write R ⊢CRW L ϕ and we say that ϕ is provable in R. Goals
for a program R are finite conjunctions of atomic formulas, and solutions are Csubstitutions that make goals derivable. In González-Moreno et al. (1999), a sound
and complete lazy narrowing calculus for goal-solving can be found.
We interpret CRWL-programs over algebraic structures consisting of posets with
bottom as carriers (i.e. sets D with a partial order ⊑D and a least element ⊥D ), whose
elements are thought of as finite approximations of possibly infinite values in the
poset’s ideal completion (Möller, 1985), and monotonic mappings from elements to
cones (non-empty subsets of a poset with bottom, downclosed w.r.t. the partial order
of the poset) as function symbol denotations reflecting possible non-determinism.
Such a mapping f: D → C(E) – where D, E are posets with bottom, and C(E) is
the set of cones of E – can be extended to a monotonic mapping f̂: C(D) → C(E),
S
defined by f̂(C) =def u∈C f(u) and also noted f by abuse of notation. In particular,
deterministic function symbols are represented by mappings f: D → I(E) computing
directed cones or ideals (i.e., cones C such that for all x, y ∈ C there exists z ∈ C
with x ⊑ z and y ⊑ z) where I(E) is the set of ideals of E. These mappings become
continuous mappings between algebraic cpos after performing the ideal completion
(for a comprehensive exposition of these notions we refer to Abramsky and Jung
(1994)). These ideas are behind the notion of CRWL-algebra.
Given a signature Σ and a set V of variable symbols, a CRWL-algebra of signature
Σ is an algebraic structure A = (DA , {cA }c∈DSΣ , {f A }f∈FSΣ ) where the carrier DA
n
is a poset with bottom ⊥A , f A is a monotonic mapping DA
→ C(DA ) for each
n
→ I(DA ) for each c ∈ DSΣn . Both f A
f ∈ FSΣn and cA is a monotonic mapping DA
and cA reduce to cones when n = 0. To ensure preservation of finite and maximal
elements in the ideal completion, we require for all u1 , . . . , un ∈ DA that there exists
v ∈ DA such that cA (u1 , . . . , un ) = hvi, where hvi is the ideal generated by v (i.e. the
set {d ∈ DA | d ⊑ v}), and if all ui are maximal (totally defined) then v must also be
maximal. The class of all CRWL-algebras of signature Σ is denoted by AlgΣ . We are
specially interested in CRWL-term algebras, which are CRWL-algebras with carrier
CTerm⊥ , ordered by the approximation ordering ‘⊑’, defined as the least partial
ordering satisfying the following properties:
(a)
(b)
⊥ ⊑ t,
c(s) ⊑ c(t)
∀t ∈ CTerm⊥ ;
if si ⊑ ti , i = 1, . . . , n, for c ∈ DSΣn , n > 0;
and fixed interpretation for constructor symbols: cA = hci, for all c ∈ DSΣ0 , and
cA (t) = hc(t)i, for all c ∈ DSΣn and n > 0. Therefore, two CRWL-term algebras of
the same signature Σ will only differ in their interpretations for the function symbols
of Σ. As a consequence of the above definition, for s, t ∈ CTerm⊥ , s ⊑ t implies
s = ⊥ or s = c(s) and t = c(t) for some c ∈ DSΣn and n > 0 with each component
si ⊑ ti . Also, for s, t ∈ CTerm⊥ , s ⊑ t is equivalent to ⊢CRWL t → s. It can be proved,
Composing programs in CRWL
195
by induction, that every θ ∈ CSubst⊥ is a monotonic mapping from CTerm⊥ to
CTerm⊥ , that is: s ⊑ t ⇒ sθ ⊑ tθ, for all s, t ∈ CTerm⊥ .
A valuation over a structure A ∈ AlgΣ is any mapping η: V → DA . η is totally
defined when η(X) is maximal for all X ∈ V. Val(A) is the set of all valuations over
A and DefVal(A) the set of all totally defined valuations. Given a valuation η we
can evaluate each partial Σ-term in A as follows:
[[ ⊥ ]] A
η
[[ X ]] A
η
[[ c ]] A
η
[[ h(e) ]] A
η
=def
=def
=def
=def
h⊥A i,
hη(X)i,
∀X ∈ V;
cA ,
∀c ∈ DSΣ0 ∪ FSΣ0 ;
A
n
n
ĥA ( [[ e1 ]] A
η , . . . , [[ en ]] η ), ∀h ∈ DSΣ ∪ FSΣ , n > 0.
In this way each partial Σ-term is evaluated to a cone. For each CRWL-algebra
A, every η ∈ Val(A), and e ∈ Term⊥ , the following properties are proved in
González-Moreno et al. (1999):
1. [[ e ]] A
η ∈ C(DA ).
2. [[ e ]] A
η ∈ I(DA ), if e is only built from deterministic functions (i.e. function
symbols interpreted by ideal valued functions).
3. [[ e ]] A
η = hvi for some v ∈ DA , if e ∈ CTerm⊥ . Moreover, when e ∈ CTerm and
η ∈ DefVal(A), v is maximal.
A
4. (Substitution Lemma) [[ eθ ]] A
η = [[ e ]] ρ , for θ ∈ CSubst⊥ , where ρ is the
uniquely determined valuation that satisfies hρ(X)i = [[ Xθ ]] A
η , for all X ∈ V.
From these results and taking into account that each substitution is equivalent to
a valuation over any CRWL-term algebra, we have the following complementary
results for term algebras:
Proposition 1
For each CRWL-term algebra A and every η ∈ Val(A) we have:
1. [[ t ]] A
η = htηi for every t ∈ CTerm⊥ ;
A
n
n
2. [[ h(t) ]] A
η = h (tη) for all h ∈ DSΣ ∪ FSΣ , n > 0, and t1 , . . . , tn ∈ CTerm⊥ ;
A
A
3. [[ eθ ]] η = [[ e ]] θη for all e ∈ Term⊥ and θ ∈ CSubst⊥ , where θη represents the
function composition η ◦ θ.
Models in CRWL are introduced from the following notion of satisfiability:
• A satisfies a reduction statement a → b under a valuation η ∈ Val(DA ), or
A
A |=η (a → b), iff [[ a ]] A
η ⊇ [[ b ]] η .
• A satisfies a joinability statement a ⊲⊳ b under a valuation η ∈ Val(DA ), or
A
A |=η (a ⊲⊳ b), iff [[ a ]] A
η ∩ [[ b ]] η contains a maximal element in DA .
• A satisfies a rule l → r ⇐ C, or A |= (l → r ⇐ C), iff A |=η C implies A |=η
(l → r), for every valuation η ∈ Val(DA ).
• A is a model of a program R, i.e., A |= R, iff A satisfies all rules in R.
CRWL-provability is sound and complete w.r.t. this model-theoretic semantics when
we consider totally defined valuations only. In González-Moreno et al. (1999) it is
proved that for any program R and any approximation or joinability statement ϕ,
R ⊢CRW L ϕ is equivatent to A |=η ϕ, for every A model of R and η ∈ DefVal(DA ).
196
J. M. Molina-Bravo and E. Pimentel
This result is achieved with the help of a CRWL-term algebra MR characterized by
the following interpretation for any defined function symbol f ∈ FSΣn , n > 0,
f MR (t) =def {r ∈ CTerm⊥ | R ⊢CRW L f(t) → r}.
MR is such that R ⊢CRW L ϕ ⇔ MR |=id ϕ for any approximation or joinability
statement ϕ. According to this result, MR is taken as the canonical model of the
program R. Also, in González-Moreno et al. (1999), it is proved that this model is
freely generated by V in the category of all models of R. This is the model-theoretical
semantics of the program R.
Given a signature Σ and a function symbol renaming ρ: Σ → Σ, for each CRWLterm algebra A = (CTerm⊥ , {cA }c∈DSΣ , {f A }f∈FSΣ ) of this signature we can define
another CRWL-term algebra Aρ = (CTerm⊥ , {cAρ }c∈DSΣ , {f Aρ }f∈FSΣ ), such that
f Aρ = ρ(f)A . The relation between evaluation and satisfaction in A and evaluation
and satisfaction in Aρ is stated by the following proposition.
Proposition 2
Given a signature Σ, for every CRWL-term algebra A of this signature, every
function symbol renaming ρ: Σ → Σ, and all θ ∈ CSubst⊥ , we have
1. (ρ(t))θ = ρ(tθ), for all t ∈ Term⊥ .
Aρ
2. [[ ρ(t) ]] A
θ = [[ t ]] θ , for all t ∈ Term⊥ .
3. A |=θ ρ(ϕ) ⇔ Aρ |=θ ϕ, for any reduction or joinability statement ϕ.
3 Fixpoint semantics
In this section we prove, for every CRWL-program R, that MR is the least fixpoint
of an operator defined over CRWL-term algebras. The approach we use here is
similar to that applied in the field of logic programming (Apt, 1990). However,
the notion of interpretation, and the corresponding mathematical aspects, have to
be reformulated in the context of CRWL-term algebras. This approach has been
also used in González-Moreno (1994) in the context of a previous formalism to
model functional-logic programming. However, this work does not deal with some
relevant aspects (e.g. non-determinism) of the CRWL-programming version we are
considering here.
Let TAlgΣ be the set of all CRWL-term algebras of a signature Σ associated
to a CRWL-program R. We can define the relationship A ⊑ B, between two
algebras A, B ∈ TAlgΣ , as f A (t) ⊆ f B (t) for all f ∈ FSΣn , when n > 0, and
f A ⊆ f B , when n = 0. This relationship is obviously a partial ordering and
(TAlgΣ , ⊑) is a poset. This poset has a bottom ⊥Σ and a top ⊤Σ characterized by
f ⊥Σ (t) = h⊥i and f ⊤Σ (t) = CTerm⊥ , respectively, for each f ∈ FSΣn and n > 0.
S
Given a subset S ⊆ TAlgΣ , the following definitions: f ⊔S (t) =def A∈S f A (t), and
T
f ⊓S (t) =def A∈S f A (t), for each f ∈ FSΣn and n > 0, characterize two CRWL-term
algebras, ⊔S and ⊓S respectively, because the union and intersection of any number
of cones are cones also, and the resulting functions in the above definitions are
obviously monotonic if f A is monotonic for all A ∈ S. Clearly, ⊔S and ⊓S are the
Composing programs in CRWL
197
least upper bound and the greatest lower bound of S, respectively. So, (TAlgΣ , ⊑) is
a complete lattice.
Valuations (substitutions) of terms in term algebras can be considered continuous
mappings from algebras to cones in the sense given by the following lemma.
Lemma 1 (Continuity of valuations in TAlgΣ )
For each term e ∈ Term⊥ and each substitution θ ∈ CSubst⊥
B
1. A ⊑ B ⇒ [[ e ]] A
θ ⊆ [[ e ]] θ , for A, B ∈ TAlgΣ .
S
A
⊔D
2. [[ e ]] θ = A∈D [[ e ]] θ , for all directed subsets D ⊆ TAlgΣ .
Another interesting result relates satisfiability of joinability statements in the least
upper bound of a directed set of term algebras with satisfiability in, at least, one of
the algebras of the set.
Lemma 2
Let C be a finite set of joinability statements and D a directed subset of TAlgΣ , then
⊔D |=θ C implies that there exists A ∈ D such that A |=θ C.
Given a CRWL-program R, with a signature Σ, we can define an algebra transformer TR : TAlgΣ → TAlgΣ , similar to the immediate consequences operator used
in logic programming, by fixing the interpretation of each function symbol f ∈ FSΣn ,
in a transformed algebra TR (A), as the result of one step applications of reduction
statements corresponding to instances – not necessarily ground – of those rules of
R, defining f, satisfied in A. We formalize this idea defining, for each f ∈ FSΣn ,
n > 0,
f TR (A) (t) =def {t | ∃(f(s) → r ⇐ C) ∈ [R]⊥ , si ⊑ ti , A |=id C, t ∈ [[ r ]] A
id } ∪ {⊥},
that is basically a union of cones [[ r ]] A
id . This definition corresponds to a monotonic
mapping because all rule instances (f(s) → r ⇐ C) ∈ [R]⊥ , applicable to arguments
t′ are also applicable to arguments t such that t′i ⊑ ti , for i = 1, . . . , n, and so
the corresponding interpretation characterizes a CRWL-term algebra. From this
definition of TR we can derive the continuity of the operator in TAlgΣ .
Proposition 3
For each program R its associated operator TR is continuous.
Thus, TR has a least fixpoint FR given by ⊔AR (that is also the least prefixpoint), where AR is the chain of CRWL-term algebras Ai , i ∈ N, such that
A0 = ⊥Σ ⊑ · · · ⊑ Ai+1 = TR (Ai ) ⊑ · · ·. FR is also denoted as TR ω (⊥Σ ) (see
Abramsky and Jung (1994)). To prove that FR coincides with MR we need two
lemmata, one characterizing the set of term models and other relating CRWLprovability with AR satisfiability.
Lemma 3 (Model characterization)
Given a program R, M is a term model for R iff TR (M) ⊑ M
Lemma 4
Given e ∈ Term⊥ and t ∈ CTerm⊥ , we have that R ⊢CRW L e → t implies Ai |=id
e → t for some Ai ∈ AR .
198
J. M. Molina-Bravo and E. Pimentel
From the above results we obtain the following proposition.
Proposition 4
For every program R, MR is the least fixpoint (and the least pre-fixpoint) of TR .
Thus, if we consider the meaning of a program R as the least fixpoint of its
associated transformer TR , then this fixpoint semantics coincides with the modeltheoretic semantics as it happens in logic programming. In fact, this semantics would
correspond to the C-semantics in Falaschi et al. (1993).
Definition 1 (Least model semantics)
For each program R we define its least model semantics as: {[ R ]}LM =def MR .
4 An algebra of CRWL-program modules
For designing large programs it is convenient to separate the whole task into
subtasks of manageable size and construct programs in a structured fashion by
combining and modifying smaller programs. This idea has been extended to many
programming languages giving rise to different notions of program module, each one
being attached to a programming paradigm. In CRWL-programming we are going
to follow an approach close to that developed in Brogi (1993) for logic programming,
where modules are open programs in the sense that function definitions in a module
can be completed with definitions for the same functions in other modules. We will
consider a global signature with bottom Σ⊥ = (DSΣ⊥ , FSΣ⊥ ) and a countable set
V of variable symbols and will construct modules and module expressions with
symbols of these sets. Σ⊥ and V will characterize the environment where modules
are written. Also we will consider all constructor symbols in DSΣ⊥ common to
all program modules as it is usual in other proposals of modularity for declarative
programming, like Brogi et al. (1994) and Orejas et al. (1997), where compositionality
and full abstraction are dealt with. With this decision we give up any possibility of
data abstraction and the only contribution of a program module to the environment
will be a set of (definition) rules for a subsignature of function symbols. We will
take this subsignature to denote the exportable resources of the module, and the set
of rules as its body. In a program module, function symbols may appear – in the rhs
of a rule – with no definition rule in this module. Although it may be assumed that
all function symbols are defined in each program module by assuming an implicit
rule f(t) → ⊥ for each function symbol f with no definition rule, these symbols
will be assumed to be provided by other modules and they will be taken to denote
the resources that have to be imported. They will be the parameters of the module.
From these considerations we propose the following definition for the notion of
module in CRWL-programming
Definition 2 (Module)
A module in CRWL-programming is a tuple < σp , σe , R > where R is a set of
program rules f(t) → r ⇐ C (r 6= ⊥), σe is the (exported) signature of function
symbols with a definition rule in R, σp is the (parameter) signature of those function
symbols with no definition rule in R that appear in any rule (i.e. they are invoked
but not defined).
Composing programs in CRWL
199
R is the body of the module and (σp , σe ) its interface. The interface of a module
could be inferred from its body if one knows which are the constructor symbols.
However, as we consider all constructor symbols common to all program modules,
we do not include an explicit declaration of these symbols in any module and have
to make explicit parameter signatures in order to distinguish between function and
constructor symbols. In this way, every symbol not occurring in σe nor σp will be a
constructor symbol. Next, we have an example of a module definition.
Example 3
This example shows a module for constructing ordered lists of natural numbers
with functions for inserting elements, checking the type of an element, and compare
natural numbers.
OrdNatList =
< {},
% Parameter signature
{isnat/1, leq/2, insert/2},
% Exported signature
{ isnat(zero)
-> true.
isnat(succ(X)) -> isnat(X).
leq(zero,zero)
-> true.
leq(zero,succ(X))
-> isnat(X).
leq(succ(X),zero)
-> false <= isnat(X) >< true.
leq(succ(X),succ(Y)) -> leq(X,Y).
insert(X,[])
-> [X]
<= isnat(X) >< true.
insert(X,[Y|Ys]) -> [X|[Y|Ys]]
<= leq(X,Y) >< true.
insert(X,[Y|Ys]) -> [Y|insert(X,Ys)] <= leq(X,Y) >< false.}>
In this module the parameter signature is empty, and symbols like zero/0, succ/1,
[]/0, [_|_]/2 with no definition rule are considered constructor symbols, because
they are not included in the parameter signature (and obviously because they occur
in arguments of left-hand sides).
We write PMod(Σ⊥ ) for the class of all program modules which can be defined
with a signature Σ⊥ , SubSig(Σ⊥ ) for the set of all subsignatures of a signature Σ⊥ ,
and Prg(Σ⊥ ) for the class of all sets of rules (programs) which can be defined with
Σ⊥ . On PMod(Σ⊥ ) we define three projections:
• par: PMod(Σ⊥ ) → SubSig(Σ⊥ ) such that par(< σp , σe , R >) = σp ,
• exp: PMod(Σ⊥ ) → SubSig(Σ⊥ ) such that exp(< σp , σe , R >) = σe , and
• rl : PMod(Σ⊥ ) → Prg(Σ⊥ ) such that rl (< σp , σe , R >) = R,
which give respectively the parameter signature, the exported signature, and the
body of a module.
4.1 Basic operations on modules
In this section we present a set of basic operations with modules that allows
us to express typical features of modularization techniques such as information
hiding/abstraction, import/export relationships and inheritance related to function
symbols as is done in Brogi (1993), but our set of operations is different and we
give syntactic definitions for it. We use three operations: union of programs, closure
200
J. M. Molina-Bravo and E. Pimentel
w.r.t. a signature and deletion of a signature, that are sufficient to express the most
extended ways of composing modules and their relationships, and we do not need
the intersection of programs, used in Brogi (1993) to model hiding, because we
directly deal with signatures in the closure. To give more flexibility in expressing
importation and instantiation, we also include a renaming operation. We define our
operations in such a way that all module expressions can be reduced to a flat module
< σp , σe , R > – where R could be an infinite set of rules. This is something like a
presentation semantics (Wirsing, 1990).
First we define the union of two modules as the module obtained as the simple
union of signatures and rules.
Definition 4 (Union)
Given two modules P1 =< σp1 , σe1 , R1 > and P2 =< σp2 , σe2 , R2 >, their union P1 ∪P2
is defined as the module < (σp1 ∪ σp2 ) \ (σe1 ∪ σe2 ), σe1 ∪ σe2 , R1 ∪ R2 >.
Each argument in this operation is considered an open program that can be
extended or completed with the other argument possibly with additional rules for
its exported function symbols.
Example 5
Let us consider the following module with a function to give change for an amount
of money. Values for coins are provided by the non-deterministic function coin/0,
whereas getcoin/1 gives different possibilities to get a coin for a fixed amount.
Finally, the function change/1 returns a list with the coins corresponding to the
change. In this example, we are assuming a predefined arithmetic with the usual
notation for natural numbers. This was not the case in Example 3.
MoneyChange =
< {_=<_/2, _-_/2},
{coin/0,getcoin/1,change/1},
{ coin -> 1. coin -> 5. coin -> 10.
getcoin(N) -> C <= coin >< C, C =< N >< true.
change(0) -> [].
change(N) -> [C|change(N-C)] <= getcoin(N) >< C. } >
We can extend this module with another module for providing new coins:
NewCoins = <{},{coin/0},{coin -> 15. coin -> 20.}>
simply by joining them to obtain
MoneyChange ∪ NewCoins =
< {_=<_/2, _-_/2},
{coin/0,getcoin/1,change/1},
{ coin -> 1. coin -> 5. coin -> 10. coin -> 15. coin -> 20.
getcoin(N) -> C <= coin >< C, C =< N >< true.
change(0) -> [].
change(N) -> [C|change(N-C)] <= getcoin(N) >< C. } >
Union of modules is idempotent, associative, commutative, and there exists a null
element: the module O =< σo , σo , ∅ >, where σo is the empty signature of function
symbols, representing the module with no rule.
Composing programs in CRWL
201
Proposition 5
The union of modules has the following properties:
1.
2.
3.
4.
P ∪ O = P, for every module P.
P ∪ P = P, for every module P.
(P ∪ P1 ) ∪ P2 = P ∪ (P1 ∪ P2 ), for all modules P, P1 and P2 .
P1 ∪ P2 = P2 ∪ P1 , for all modules P1 and P2 .
The second operation is the closure of a module w.r.t. a given signature σ. This
operation makes accessible the signature σ in an extensional way (i.e. only provable
approximations can be used) and hides the rest. To define this operation, we need
to introduce the notion of canonical rewrite rule.
Definition 6 (Canonical rewrite rule)
Given a term f(t), with f ∈ FSΣn and each ti ∈ CTerm⊥ , and r ∈ CTerm⊥ , we
define the canonical rewrite rule crr(f(t), r) which reduces f(t) to r, as the rule
′
f(t ) → r ⇐ C, constructed by substituting in t each occurrence of a repeated
variable X or ⊥ with a fresh variable Y and adding in C a joinability statement
X ⊲⊳ Y for each occurrence of a repeated variable X, and a statement X ⊲⊳ X for
each variable X in r and each variable with only one occurrence in t.
′
In this way we obtain a program rule (with t linear and each t′i ∈ CTerm) from
which f(t) → r can be proved, because for θt ∈ CSubst⊥ such that θt (Y ) = X for
each fresh variable Y that substitutes an occurrence of X in t, θt (Y ) = ⊥ for each
fresh variable Y that substitutes an occurrence of ⊥, and θt (X) = X for all other
′
variables, Cθt always can be proved and (f(t ) → r)θt is f(t) → r.
Example 7
The canonical rewrite rule which reduces f(⊥, b(X, Y ), X) to a(X, Z) is:
f(V , b(X, Y ), X1) → a(X, Z)
⇐ {X1 ⊲⊳ X, Y ⊲⊳ Y , Z ⊲⊳ Z},
and the associated substitution θt is such that θt (X1) = X, θt (V ) = ⊥, and θt (W ) =
W for all other variables W . In this case Cθt = {X ⊲⊳ X, Y ⊲⊳ Y , Z ⊲⊳ Z} and
all these joinability statements can be trivially derived from (RR) and (Jo), and
therefore f(⊥, b(X, Y ), X) → a(X, Z) by the (OR) rule.
Now, we can define the closure of a module as follows.
Definition 8 (Closure w.r.t. a signature)
σ
Given a module P =< σp , σe , R >, its closure P w.r.t. a signature of function
symbols σ is defined as the module:
< σo , σe′ , {crr(f(t), r) | f/n ∈ σ, r ∈ CTerm⊥ , r 6= ⊥, R ⊢CRW L f(t) → r} >,
where σo denotes the empty signature of function symbols, ti ∈ CTerm⊥ for each
component of the tuple t, and σe′ is the corresponding exported signature.
The closure of a module is a module with a possibly infinite set of rules (although
the exported signature is always finite) equivalent to the union of the graphs in MP
of all functions defined in P and contained in σ. Note that σe′ ⊆ σe ∩ σ because
202
J. M. Molina-Bravo and E. Pimentel
a function in σe ∩ σ that depends on functions in the parameter signature could
remain with no definition rule – or with the only rule f(t) → ⊥ – after closing the
σe
module. As a syntactic simplification we will write P instead of P for each module
P =< σp , σe , R >.
Example 9
Let us consider the following module about week days, where two functions are
defined to get the next day and the day before of a given day.
WeekDays = < {},
{next/1,before/1},
{ next(mo) -> tu.
next(tu) -> we.
next(th) -> fr.
next(fr) -> sa.
next(su) -> mo.
before(X) -> Y <= next(Y) >< X. }
next(we) -> th.
next(sa) -> su.
>
The closure of this module w.r.t. its whole exported signature is the module
WeekDays = < {},
{next/1,before/1},
{ next(mo) -> tu.
next(tu) -> we.
next(we) -> th.
next(th) -> fr.
next(fr) -> sa.
next(sa) -> su.
next(su) -> mo.
before(tu) -> mo. before(we) -> tu. before(th) -> we.
before(fr) -> th. before(sa) -> fr. before(su) -> sa.
before(mo) -> su. }
>
Closure w.r.t. a signature is in some way the counterpart of the encapsulation
operation ‘∗’ in Brogi (1993), but it is more general because it has a twofold effect:
hiding all rules in the module and restricting the visible signature, so we need no
intersection of modules – as is needed in Brogi (1993) – to restrict visibility in a
closed module. Variables and bottom can appear in the rules of a closed module,
but no functions in the parameter signature.
Proposition 6
Closure of modules has the following properties, where σ, σ1 and σ2 are signatures
of function symbols,
1.
2.
3.
4.
5.
σ
P = O, for every module P and every signature σ such that σ ∩ exp(P) = σo .
σ
O = O, for every signature σ and the null module O.
σ1 ∪σ2
σ1
σ2
P
= P ∪ P , for every module P and signatures σ1 , σ2 .
σ1 σ2
σ1 ∩σ2
σ2 σ1
P
=P
= P , for every module P and signatures σ1 , σ2 .
σ
σ
σ
P1 ∪ P2 = P1 ∪ P2 , for modules P1 and P2 defining disjoint signatures
and such that neither P1 nor P2 use the signature defined in the other module.
Our third operation is the deletion of a signature in a module.
Definition 10 (Deletion of a signature)
Given a module P =< σp , σe , R >, the deletion in P of a signature of function
symbols σ produces the module P \ σ =def < σp′ , σe \ σ, R \ σ >, where R \ σ denotes
the set of those rules in R defining function symbols not appearing in σ, and σp′
denotes the corresponding parameter signature.
Composing programs in CRWL
203
We do not give an explicit expression for par(P \ σ) in terms of par(P) because
new parameters can appear and old ones can disappear with the deletion of rules
in rl (P). However, par(P \ σ) ⊆ σp ∪ (σe ∩ σ) is satisfied.
Example 11
In the module OrdNatList of Example 3 we can delete or abstract the signature
{isnat/1,leq/2} to obtain the following parameterized module
OrdNatList\{isnat/1,leq/2} =
< {isnat/1,leq/2},
{insert/2},
{ insert(X,[])
-> [X]
<= isnat(X) >< true.
insert(X,[Y|Ys]) -> [X|[Y|Ys]]
<= leq(X,Y) >< true.
insert(X,[Y|Ys]) -> [Y|insert(X,Ys)] <= leq(X,Y) >< false. } >
The resulting module is now parameterized by the two symbol functions isnat/1
and leq/2, whereas only the function insert/2 is exported.
This operation recalls the undefine clause in the object-oriented language Eiffel,
and we will use it (combined with the union) to perform inheritance with overriding.
Note the differences between the deletion of a signature and the closure w.r.t. a
signature. The former operation removes rules defining function symbols in the
signature – but not those rules containing invocations in their rhs or condition –
whereas the latter only hides the definitions of the functions in the signature, but
maintains their consequences – hiding all other functions.
Proposition 7
The deletion of a signature (of function symbols) in a module has the following
properties, where σ, σ1 and σ2 are signatures of function symbols,
1.
2.
3.
4.
5.
6.
P \ σ = O, for every module P and every σ such that exp(P) ⊆ σ.
P \ σ = P, for every module P and every σ such that exp(P) ∩ σ = σo .
(P \ σ1 ) \ σ2 = P \ (σ1 ∪ σ2 ) = (P \ σ2 ) \ σ1 , for all modules P and σ1 , σ2 .
(P1 ∪ P2 ) \ σ = (P1 \ σ) ∪ (P2 \ σ), for all modules P1 , P2 and signatures σ.
σ1
(σ1 \σ2 )
, for all modules P and signatures σ1 , σ2 .
(P ) \ σ2 = P
σ
P = P \ (σe \ σ), for a module P, with exported signature σe , and all σ.
Finally, we introduce a renaming operation that allows us to change function
symbols with other function symbols of the same arity, in the global signature Σ⊥ .
Therefore, given a module P and a function symbols renaming ρ, we define the
renaming of P by ρ as a new module ρ(P) where rules are conveniently renamed.
The following definition formalizes this idea.
Definition 12 (Renaming)
Given a module P =< σp , σe , R > and a function symbol renaming ρ, P renamed
by ρ is the module ρ(P) =def < ρ∗ (σp ) \ ρ∗ (σe ), ρ∗ (σe ), ρ∗ (R) >, where ρ∗ (σ) is the
signature resulting from applying ρ to all symbols in σ, and ρ∗ (R) is the set of rules
resulting from applying ρ to all rules in R.
The following example illustrates the usefulness of this operation to adequate
parameter names of a module.
204
J. M. Molina-Bravo and E. Pimentel
Example 13
In the module OrdNatList\{isnat/1,leq/2} of Example 11 we can rename the
function symbol isnat/1 with the new name isbasetype/1 to obtain a more
appropriate parameterized module
OrdList = {isnat/1 -> isbasetype/1}(OrdNatList\{isnat/1,leq/2}),
where we have denoted the corresponding renaming function ρ as the set of pairs
f/n → ρ(f/n) such that f/n 6= ρ(f/n). This module has the following appearance
OrdList =
<{isbasetype/1,leq/2},
{insert/2},
{insert(X,[])
-> [X]
<= isbasetype(X) >< true.
insert(X,[Y|Ys]) -> [X|[Y|Ys]]
<= leq(X,Y) >< true.
insert(X,[Y|Ys]) -> [Y|insert(X,Ys)] <= leq(X,Y) >< false.} >
Now, the parameters become isbasetype/2 and leq/2.
We will use this operation to change function names in exportation, importation
and, specially, in instantiation for matching function names in the parameter signature of a module with function names in the exported signature of another module.
See section 4.2 for some illustrative examples.
Proposition 8
Renaming of modules has the following properties, where ρ, ρ1 and ρ2 are function
symbol renamings,
1.
2.
3.
4.
5.
6.
ι(P) = P, for every module P, where ι is the identity renaming.
ρ(O) = O, for every ρ.
ρ2 (ρ1 (P)) = (ρ2 ◦ ρ1 )(P), for all modules P and all ρ1 , ρ2 .
ρ(P1 ∪ P2 ) = ρ(P1 ) ∪ ρ(P2 ), for all modules P1 , P2 and all ρ.
σ
ρ∗ (σ)
, for all modules P, signatures σ and injective ρ.
ρ(P ) = ρ(P)
ρ(P \ σ) = ρ(P) \ ρ∗ (σ), for all modules P, signatures σ and injective ρ.
4.2 Other modular constructions in CRWL-programming
Our notion of module is basically that of a program inside a context made up of other
programs providing explicit rules for function symbols and implicit declarations of
constructor symbols, all together defining a global signature Σ⊥ . In this section, we
will show how the operations that we have defined above can be used to model typical
module interconnections used in conventional modular programming languages. We
will introduce new operations with modules for these relationships, but all these will
be defined as derived expressions from the basic set. These expressions will reflect
the relationship between the module denoted by the expression and its component
modules, and the resulting modules will be interpreted as flat modules in all cases.
The closure of a module M w.r.t. a signature σ gives a form of encapsulation,
hiding those function symbols in M that are not in σ, and making the function
symbols in M and σ visible but only in an extensional way, i.e. by the results –
Composing programs in CRWL
205
as partial constructor Σ-terms – of the function applications to constructor Σ-terms
(including variables). Thus, we can provide an export with encapsulation operation
σ
‘’ over modules, in this simple way, σM =def M .
The union of modules reflects the behavior of some logic programming systems
that allow adding new programs – saved in separate files – to the main database. With
this operation, but modifying one of its arguments, we can express different forms
of importation and instantiation. We can define an import operation ≪ between
modules as the union of a module M – representing the body of the importing
module – with the closure of the imported module N as M ≪ N =def M ∪ N.
Module M ≪ N imports N, which means that only the consequences of the
functions defined in N are imported, and not their rules. When exp(M) ∩ exp(N) =
σo we have a typical importation because functions defined in N are only reduced
in N. We can also express selective importation of a signature σ from N by
combining importation with exportation to restrict the visible signature of the
imported module, as M ≪ (σN), with σ ⊆ exp(N). This expression is equivalent
σ
to M ∪ N by Proposition 6(4). Multiple importation or (selective) importation
from several modules can be written as (. . . (M ≪ (σ1 N1 )) . . .) ≪ (σk Nk ),
where the importation order is not relevant by Propositions 5(3,4) and 6(4,5). It
can be easily proved that this expression is equivalent to the single importation
M ≪ ((σ1 N1 ) ∪ . . . ∪ (σk Nk )). Importation with renaming can be expressed by an
expression of the form M ≪ ρ(σN), with σ ⊆ exp(N) and an injective function
symbol renaming ρ (see Proposition 8(5)). By the properties of renaming this
σ
expression is equivalent to M ≪ (ρ∗ (σ)ρ(N)) and can be reduced to M ∪ ρ(N ).
Example 14
Let us consider the module OrdList in Example 13 and the new module
OrdNat =
< {},
{isnat/1, leq/2, geq/2},
{ isnat(zero)
-> true.
isnat(succ(X)) -> isnat(X).
leq(zero,zero)
-> true.
leq(zero,succ(X))
-> isnat(X).
leq(succ(X),zero)
-> false <= isnat(X) >< true.
leq(succ(X),succ(Y)) -> leq(X,Y).
geq(X,Y)
-> leq(Y,X). } >
where we define the predicate isnat/1 and the two order relationships leq/2 (less
than or equal to) and geq/2 (greater than or equal to). The importation
OrdList ≪ {isnat/1 -> isbasetype/1}(OrdNat)
is a module with an infinite number of rules for isbasetype/1, leq/2 and geq/2
(all possible reductions to true or false), that behaves as calls to isbasetype/1 and
leq/2 are reduced in OrdNat as calls to isnat/1 and leq/2 itself respectively.
Thus a typical program M with a hierarchical structure in the sense of standard
modular programming, i.e., importing from several modules N1 , . . . , Nk , possibly
with renaming, can be built up from a plain program P – its body – and the
206
J. M. Molina-Bravo and E. Pimentel
imported modules as M = P ≪ (ρ1 (σ1 N1 )∪. . .∪ρk (σk Nk )), with σ1 ⊆ exp(N1 ),
. . . , σk ⊆ exp(Nk ) and par(P) ⊆ (ρ∗1 (σ1 )∪. . .∪ρ∗k (σk )). This expression can be reduced
σ1
σk
to P ∪ ρ1 (N1 ) ∪ . . . ∪ ρk (Nk ).
Because our basic modules can be parameterized, we can instantiate function
symbols of the parameterized signature of a module M with function symbols,
of the same arity but different name, exported by other module N, simply by
renaming suitably the parameters of M to fit (a part of) the exported signature
of N. Thus we obtain an instantiation operation that we denote M[N, ρ] and
define as M[N, ρ] =def ρ(M) ≪ N, where ρ is the function symbol renaming
that characterizes the instantiation. This operation makes sense when ρ∗ (par(M)) ∩
exp(N) 6= σo . When par(ρ(M)) ⊆ exp(N) the instantiation is total and is partial in
another case. Note that instantiation can be seen as a special form of importation.
The difference between a (renamed) importation M ≪ ρ(N) and an instantiation
ρ(M) ≪ N is that in the former, symbols in the parameter signature of M refer
to actual names in the exported signature of the imported module N (renamed by
ρ), whereas in the latter, symbols in the parameter signature of M behave as true
parameters being replaced by ρ with actual values of the exported signature of N.
Example 15
Let us consider again the module OrdList in Example 13 and the module OrdNat
defined in Example 14. The instantiation
OrdList[OrdNat,{isbasetype/1 -> isnat/1, leq/2 ->geq/2}]
is equivalent to a module, also with an infinite number of rules, but defining the
predicates isnat/1 and geq/2 instead of isbasetype/1 and leq/2 , respectively.
Deletion of a signature σ in a module removes all rules defining function symbols
in that signature but maintains the occurrences of these symbols in the rhs of the
other rules. This operation can be used to abstract a signature σ from a module
M in this way, M[σ] =def M \ σ. This abstraction operation makes sense when
σ ⊆ exp(M) and each function symbol in σ appears in some rule of rl (M \ σ).
This operation is very useful for making generic modules from concrete ones but
unfortunately it is not implemented in conventional modular programming systems.
As an example of the use of this operation we refer to Example 11. Also, with the
deletion operation, we can model a sort of inheritance relationship between modules.
Inheritance with overriding may be captured by means of union and deletion of a
signature in this way, M isa N =def M ∪ (N \ exp(M)). Module M isa N inherits
all functions in N – with their rules – not defined in M and uses the rules of M
for all functions defined in M, overriding the definition rules in N, for common
functions. In this case, overriding is carried out by deleting the common signature
of the inherited module before adding it to the derived module.
Example 16
Let us consider a module defining some operations on polygonal lines and parameterized w.r.t. an addition operation _+_/2, a predicate ispoint/1 to test if something
Composing programs in CRWL
207
is a point, and operations distance/2 and translatepoint/2 for computing the
distance between points and the point resulting of applying a translation, given by
a vector (its second argument), to another point (its first argument).
Polygonal =
<{_+_/2, ispoint/1, distance/2, translatepoint/2 },
{perimeter/1,translate/2 },
{perimeter([P1])
-> zero <= ispoint(P1) >< true.
perimeter([P1|[P2|Ps]]) -> distance(P1,P2)+perimeter([P2|Ps]).
translate([P1],V)
-> [translatepoint(P1,V)].
translate([P1|[P2|Ps]],V) -> [translatepoint(P1,V)|translate([P2|Ps],V)].} >
(where we suppose that distance/2 and translatepoint/2 check that their arguments are points). Let us also consider another module defining some operations
on squares and also parameterized w.r.t. a multiplication operation _*_/2, and the
above operations ispoint/1 and distance/2.
Square =
< {_*_/2, ispoint/1, distance/2},
{issquare/1, side/1, perimeter/1, surface/1},
{issquare([P1,P2,P3,P4]) -> true <= distance(P1,P2) >< distance(P2,P3),
distance(P2,P3) >< distance(P3,P4),
distance(P1,P2) >< distance(P3,P4).
side([P1,P2,P3,P4]) -> distance(P1,P2) <= issquare([P1,P2,P3,P4]) >< true.
perimeter(C) -> 4*side(C)
<= issquare(C) >< true.
surface(C)
-> side(C)*side(C) <= issquare(C) >< true.} >.
With these modules we could define a new module SquarePolygone making module
Square inherit from Polygonal,
SquarePolygone = Square isa Polygonal.
The resulting module would be
SquarePolygone =
< {_+_/2, _*_/2, ispoint/1, distance/2, translatepoint/2},
{issquare/1, side/1, perimeter/1, surface/1, translate/2},
{issquare([P1,P2,P3,P4]) -> true <= distance(P1,P2) >< distance(P2,P3),
distance(P2,P3) >< distance(P3,P4),
distance(P1,P2) >< distance(P3,P4).
side([P1,P2,P3,P4]) -> distance(P1,P2) <= issquare([P1,P2,P3,P4]) >< true.
perimeter(C) -> 4*side(C)
<= issquare(C) >< true.
surface(C)
-> side(C)*side(C) <= issquare(C) >< true.
translate([P1],V)
-> [translatepoint(P1,V)].
translate([P1|[P2|Ps]],V) -> [translatepoint(P1,V)|translate([P2|Ps],V)].} >.
Note that perimeter/1, defined in the module Polygonal, has been redefined with
the version of the module Square. The function translate/2 has been inherited
from Polygonal.
5 A compositional semantics
A module is basically a program because its interface can be extracted from its
set of rules when we know the data constructor symbols, and operations defined
on modules are operations on their sets of rules, i.e. operations on programs. The
difference between a program and a program module is that a module can be
thought of as a program piece that can be assembled with other pieces to build
larger programs (this is one of the main reasons of making explicit their interfaces).
208
J. M. Molina-Bravo and E. Pimentel
With this idea in mind, the model-theoretic semantics defined for CRWL-programs
is not suitable for program modules because it is not compositional w.r.t. the
operations defined over modules as we can see in the following example.
Example 17
Let Σ be a signature ({a/0, b/0, c/0}, {p/1, r/1}), and modules P1 and P2 with the
following sets of rules: rl(P1 ) = {p(a) → c} and rl(P2 ) = {p(a) → c, r(b) → c ⇐
p(b) ⊲⊳ c}. These modules have the same model-theoretic semantics, MP1 = MP2 ,
which is the CRWL-algebra A with functions pA and rA such that
pA (a) = {c, ⊥}, pA (b) = pA (c) = pA (⊥) = {⊥}, pA (X) = {⊥}, ∀X ∈ V
rA (a) = {⊥},
rA (b) = rA (c) = rA (⊥) = {⊥}, rA (X) = {⊥}, ∀X ∈ V.
However, their unions with Q, such that rl(Q) = {p(b) → c}, have different modeltheoretic semantics. The intended model of P1 ∪ Q has a function rMP1 ∪Q such that
rMP1 ∪Q (b) = {⊥}, whereas rMP2 ∪Q (b) = {c, ⊥}. So, MP1 ∪Q 6= MP2 ∪Q .
The compositionality of the semantics of a programming language is particularly
relevant when modularity is involved. In fact, one of the most critical aspects in
modular systems is the possibility of making a separate compilation of modules,
and this can only be made in the presence of some kind of compositionality.
To study the compositionality and full abstraction of a semantics, we have to
clearly set out these notions. We will adopt the approach proposed in Brogi and
Turini (1995), where compositionality and full abstraction are defined in terms of
the equivalence relation between programs induced by the semantics.
Definition 18 (Compositional relation)
Given an equivalence relation ≡ defined between programs, an observation function
Ob defined for programs, and a set Oper of operations with programs, we say that
1. ≡ preserves Ob iff for all programs P and Q, P ≡ Q ⇒ Ob(P) = Ob(Q);
2. ≡ is a congruence w.r.t. Oper iff for all programs Pi and Qi and all O ∈ Oper,
Pi ≡ Qi , for i = 1, . . . , n, implies O(P1 , . . . , Pn ) ≡ O(Q1 , . . . , Qn );
3. ≡ is compositional w.r.t. (Ob, Oper) iff it is a congruence w.r.t. Oper and
preserves Ob.
To set the notion of full abstraction for an equivalence relation, we need some way
of distinguishing programs and for that reason we introduce the notion of context.
Given a set of operations on programs Oper, and a metavariable X, we define contexts C [[ X ]] inductively as follows: X and each program is a context, also for each
operation O ∈ Oper with n program arguments and C1 , . . . , Cn contexts, O(C1 , . . . , Cn )
is a context. Two programs P and Q are distinguishable under (Ob, Oper) if there exists a context C [[ X ]] such that C [[ P ]] and C [[ Q ]] have different external behavior,
i.e. Ob(C [[ P ]] ) 6= Ob(C [[ Q ]] ). When P and Q are indistinguishable under (Ob, Oper)
we will write P ∼
=Ob,Oper Q, i.e. for all contexts C, Ob(C [[ P ]] ) = Ob(C [[ Q ]] ).
Definition 19 (Fully abstract relation)
An equivalence relation ≡ is fully abstract w.r.t. (Ob, Oper) iff for all programs P
and Q, P ∼
=Ob,Oper Q ⇒ P ≡ Q.
Composing programs in CRWL
209
A semantics S for a programming language provides a meaning for programs
and also induces an equivalence relation ≡S between programs: two programs are
equivalent iff they have the same meaning in this semantics. This equivalence relation
is used for defining compositionality and full abstraction for semantics.
Definition 20 (Compositional and fully abstract semantics)
A semantics S is compositional or fully abstract w.r.t. (Ob, Oper) iff its corresponding
relation ≡S is compositional or fully abstract, respectively, w.r.t. (Ob, Oper).
Obviously, for each pair (Ob, Oper) there exits a compositional and fully abstract
relation between programs, the relation P ≡(Ob,Oper) Q iff Ob(C [[ P ]] ) = Ob(C [[ Q ]] ),
for every context C [[ X ]] . For each compositional equivalence relation ≡, it is easy to
see that P ≡ Q ⇒ P ≡(Ob,Oper) Q, and for each fully abstract equivalence relation ≡,
P ≡(Ob,Oper) Q ⇒ P ≡ Q. Thus, ≡(Ob,Oper) will be the only equivalence relation which
is both compositional and fully abstract w.r.t. (Ob, Oper). And the more adequate semantics for programs (w.r.t. (Ob, Oper)) will be a semantics that induces this relation.
5.1 The T-semantics
To find a compositional semantics we may think about programs as open in the sense
that we can build up programs from other programs adding rules for new functions
and also for already defined functions (of the signature Σ we were in) and imagine
them as algebra transformers as is done in Mancarella and Pedreschi (1988) and
Brogi (1993). The operator TP considered as a function TAlgΣ → TAlgΣ is a good
candidate for the intended meaning of a program P. First, we have to note that the
set [TAlgΣ → TAlgΣ ] of all continuous functions from TAlgΣ to TAlgΣ , ordered by
the relation T1 ⊑ T2 iff T1 (A) ⊑ T2 (A), for all A ∈ TAlgΣ , with the least upper
bound and the greatest lower bound of a finite set {Ti }i∈I of functions pointwise
defined as (⊔i∈I Ti )(A) = ⊔i∈I (Ti (A)) and (⊓i∈I Ti )(A) = ⊓i∈I (Ti (A)) respectively,
and with bottom T⊥ and top TΣ such that T⊥ (A) = ⊥Σ and TΣ (A) = ⊤Σ ,
for all A ∈ TAlgΣ , is a complete lattice as a consequence of (TAlgΣ , ⊑) being a
complete lattice. Now, we can associate a program with the corresponding immediate
consequence operator, instead of its least fixpoint.
Definition 21 (T-semantics)
We define the T-semantics {[ P ]}T by denoting the meaning of a program P by its
algebra transformer TP , where TP is intended as Trl(P) .
This semantics entails the following equivalence relation on programs: P ≡T
Q ⇔def TP = TQ . Thus, two programs are ≡T -equivalent if both define the same
immediate consequences operator. In this context, and coinciding with logic programming, a natural choice of the observable behavior of a program R is its modeltheoretic semantics. So we will adopt as observation function Ob(R) =def MR .
Notice that MR captures the graphs of all functions defined in R, whereas functions
not included in the program are considered totally undefined (their images only
can be reduced to ⊥). The semantics {[ · ]}T is compositional w.r.t. this observation
σ
function and the set of operations Oper = {∪, (·) , (·)\σ, ρ(·)}. We can prove this fact
by proving that {[ · ]}T is homomorphic in the following sense.
210
J. M. Molina-Bravo and E. Pimentel
Theorem 22
Given a global signature Σ and a countable set of variable symbols V, for all
programs P, P1 and P2 defined over Σ, every subsignature of function symbols
σ ⊆ FSΣ , and every function symbol renaming ρ, we have the following results
(a)
(b)
(c)
(d)
{[ P1 ∪ P2 ]}T
σ
{[ P ]}T
{[ P \ σ ]}T
{[ ρ(P) ]}T
= {[ P1 ]}T ⊔{[ P2 ]}T ;
= λA · ( {[ P ]}ω
T (⊥Σ ))|σ ;
= {[ P ]}T ⊓ Texp(P)\σ ;
= Tρ−1 ◦ {[ P ]}T ◦Tρ ;
where, for every algebra A ∈ TAlgΣ and every subsignature σ ⊆ FSΣ , A|σ is the term
algebra characterized by f A|σ (t) = f A (t), for f/n ∈ σ, and f A|σ (t) = {⊥}, otherwise.
For each subsignature σ ⊆ FSΣ , Tσ is the constant algebra transformer that, for all
A ∈ TAlgΣ produces the same term algebra ⊤σ characterized by f ⊤σ (t) = CTerm⊥ ,
for f/n ∈ σ, and f ⊤σ (t) = {⊥}, otherwise. And, for each rename ρ, Tρ and Tρ−1 are
the algebra transformers defined by Tρ (A) = Aρ and Tρ−1 (A) = Aρ−1 where Aρ
and Aρ−1 are the term algebras characterized by
⊔{g A | f = ρ(g)}, when this set is not empty,
Aρ−1
Aρ
A
and f
=
f = ρ(f)
otherwise,
f ⊥Σ
for every function symbol f in FSΣ .
Thus, the meaning of the union of two programs (a) can be extracted from the
meaning of each one, the meaning of the closure of a program (b) is obtained from
the fixpoint of the program semantics, and deleting a signature from a program (c) is
semantically equivalent to the intersection of the program semantics with an algebra
transformer which depends on the exported signature of the program. Nevertheless,
the intersection we are mentioning here is not an operation over programs (as in
Brogi (1993)) but an operation on algebra transformers. The meaning of a renamed
program (d) can be obtained as the composition of the meaning of the program
with two algebra transformers associated with the renaming and its reverse.
Corollary 1 (Compositionality of {[ · ]}T )
σ
The semantics {[ · ]}T is compositional with respect to (Ob, {∪, (·) , (·)\σ, ρ(·)}).
As the above corollary states, {[ · ]}T is compositional w.r.t. union, closure, deletion
and renaming, when the canonic model of a program is taken as its observable
behavior. However, the following example shows that it is not fully abstract.
Example 23
Let Σ be a signature ({c/0, d/0}, {f/0}) and let P and Q be the modules such
that rl(P) = {f → c, f → d} and rl(Q) = {f → c, f → d ⇐ f ⊲⊳ c}. They are
σ
indistinguishable under {∪, (·) , (·)\σ, ρ(·)}, but they are not ≡T -equivalent. In fact,
TP (⊥Σ ) 6= TQ (⊥Σ ) because f TP (⊥Σ ) = {c, d, ⊥} whereas f TQ (⊥Σ ) = {c, ⊥}.
The T-semantics distinguishes more than the model-theoretic semantics, since the
immediate consequence operator captures what is happening in each reduction step,
but the non-full abstraction result means that this semantics distinguishes more than
necessary. It is too fine. In the next section we will try a coarser semantics – also
211
Composing programs in CRWL
studied in logic programming (Brogi and Turini, 1995) – defined from the sets of
pre-fixpoints of T.
6 A fully abstract semantics
In this section, a fully abstract semantics is presented, which is also compositional
except for the deletion operation. For a better motivation, we will not introduce this
semantics directly. Instead, we will define a first approximation, the so-called term
model semantics (Definition 24), which only is compositional (w.r.t. the union, closure
and renaming operations), and then we will obtain the full abstraction property by
restricting the term models (Definition 29).
6.1 The term model semantics
Formally, we will introduce the first semantics by directly considering the corresponding equivalence relation.
Definition 24 (Model equivalence)
Two programs P and Q are model-equivalent, P ≡M Q, iff their algebra transformers
have the same pre-fixpoints.
By Lemma 3 this means that two programs are equivalent iff they have the same
term models. This equivalence relation corresponds to the following semantics:
{[ P ]}M =def {M | M is a term model of P}
which will be called loose model-theoretic semantics, or simply term model semantics.
To derive the corresponding result about compositionality, we need an auxiliary
property about Tρ and Tρ−1 .
Lemma 5
Given two term algebras A, B ∈ TAlgΣ , for every function symbol renaming ρ,
Aρ−1 ⊑ B ⇔ A ⊑ Bρ or, equivalently, Tρ−1 (A) ⊑ B ⇔ A ⊑ Tρ (B).
This lemma claims that Tρ−1 is, essentially, the reverse operator for Tρ .
Theorem 25 (Compositionality of {[ · ]}M )
For all programs P, Q, Pi , Qi ,
1.
2.
3.
4.
P ≡M Q implies Ob(P) = Ob(Q).
Pi ≡M Qi for i = 1, 2, implies P1 ∪ P2 ≡M Q1 ∪ Q2 .
σ
σ
P ≡M Q implies P ≡M Q , for every signature σ.
P ≡M Q implies ρ(P) ≡M ρ(Q), for every function symbol renaming ρ.
σ
Therefore, the semantics {[ · ]}M is compositional w.r.t. (Ob, {∪, (·) , ρ(·)}).
Unfortunately, this semantics is not compositional w.r.t. deletion.
212
J. M. Molina-Bravo and E. Pimentel
Example 26
Let Σ be the signature ({a/0, b/0}, {f/0, g/0}) and let P and Q be two modules
with rules rl(P) = {f → a, g → b} and rl(Q) = {f → a, g → b ⇐ f ⊲⊳ a}. Both
modules have the same term models, those term algebras A with a ∈ f A and
b ∈ g A . But by deleting f/0 in each module we have P \ {f/0} and Q \ {f/0} with
rl(P \ {f/0}) = {g → b} and rl(Q \ {f/0}) = {g → b ⇐ f ⊲⊳ a}, and now ⊥Σ is a
model of Q \ {f/0} whereas it is not a model of P \ {f/0}.
For a different reason, the semantics {[ · ]}M is not fully abstract.
Example 27
Let Σ be the signature ({a/0}, {f/0, g/1}) and let P and Q be two modules with
rules rl(P) = {f → a ⇐ g(a) ⊲⊳ a} and rl(Q) = {f → a ⇐ g(X) ⊲⊳ a}, where the rule
in P is an instance of the rule in Q. Obviously, both modules are indistinguishable
but they do not have the same term models. In fact, if we consider the algebra A
such that: f A = {⊥}, g A (X) = {a, ⊥} and g A (a) = {⊥}, A is a model of P but it is
not a model of Q.
6.2 The consistent term model semantics
To prove the full abstraction property we need to consider a different equivalence
relation (i.e. semantics). If we observe the above counter-example, we can see that,
for the term algebra A used to distinguish {[ P ]}M from {[ Q ]}M , g A (X) = {a, ⊥}
and g A (a) = {⊥}; that is, A is such that the instantiation of the variable X derives
in a loss of information for the interpretation of g because g A (Xθ) is smaller than
(g A (X))θ, for θ = {X/a}. In general, the notion of term algebra (see section 2) does
not impose any relation between g A (t̄θ) and (g A (t̄))θ. This is not reasonable if we
take into account the role of term algebras when they are used to model programs.
On the contrary, the interpretation of a function symbol (in a term algebra) applied
to arguments with variables must be related to the interpretation of the same
function symbol when these variables are instantiated. With this idea in mind, we
introduce the notion of consistency in a term algebra.
Definition 28 (Consistency of term algebras)
A term algebra A ∈ TAlgΣ is consistent iff for every f ∈ FSΣn and ti ∈ CTerm⊥
(i = 1, . . . , n), f A (tθ) ⊇ (f A (t))θ for all θ ∈ CSubst, where (f A (t))θ stands for the set
{uθ | u ∈ f A (t)}.
We denote by CTAlgΣ the family of all consistent term algebras. Note that
consistency is only required for total substitutions (i.e. substitutions which do not
include partial constructor terms). This is due to the special treatment of ⊥, which is
considered as lack of information. The notion of consistency here introduced is close
to that of closure under substitutions defined for interpretations in Apt (1996), and is
also related with the notion of C-interpretation considered in Falaschi et al. (1993),
but our requirements are weaker than those. To justify the reasonable nature of
consistent term algebras we will prove several desirable properties. For instance, the
immediate consequences operator maps consistent algebras into consistent algebras,
and the canonical model of a program is consistent.
213
Composing programs in CRWL
Lemma 6
A
For every A ∈ CTAlgΣ , r ∈ Term⊥ , and θ ∈ CSubst, [[ r ]] A
id θ ⊆ [[ rθ ]] id .
Proposition 9
Given a program P, if A ∈ CTAlgΣ , then TP (A) ∈ CTAlgΣ .
Proposition 10
Given a program P, the canonical term model MP is consistent.
Now, we may define an equivalence relation based only on consistent term models.
Definition 29 (Consistent model equivalence)
Two programs P and Q are consistent model-equivalent, P ≡CM Q, iff their have
the same consistent models.
This equivalence is clearly weaker than the model equivalence and corresponds to
the following semantics:
{[ P ]}CM =def {M | M is a consistent term model of P}
which will be called loose consistent model-theoretic semantics, or simply consistent
term model semantics. Obviously, {[ P ]}CM = {[ P ]}M ∩ CTAlgΣ , and the compositionality property of this semantics may be obtained in a similar way as the
compositionality of the term model semantics.
Theorem 26 (Compositionality of {[ · ]}CM )
For all programs P, Q, Pi , Qi ,
1.
2.
3.
4.
P ≡CM Q implies Ob(P) = Ob(Q).
Pi ≡CM Qi for i = 1, 2, implies P1 ∪ P2 ≡CM Q1 ∪ Q2 .
σ
σ
P ≡CM Q implies P ≡CM Q , for every signature σ.
P ≡CM Q implies ρ(P) ≡CM ρ(Q), for every function symbol renaming ρ.
σ
Therefore, the semantics {[ · ]}CM is compositional w.r.t. (Ob, {∪, (·) , ρ(·)}).
Example 26 also illustrates the non-compositionality of {[ · ]}CM w.r.t. the deletion
operation because the programs P and Q only define functions without arguments.
However, this semantics is fully abstract; to prove this fact, we need an auxiliary
result, showing how a (minimal ) program P can be constructed from a consistent
term algebra A and an element t ∈ [[ r ]] A
id such that A is a model of P and
P
.
Proposition
11
formalizes
this
idea. In order to simplify the proof of
t ∈ [[ r ]] M
id
this result, we will prove some properties about the notion of canonical rewrite rule
already introduced in Definition 6.
Lemma 7
For each canonical rewrite rule crr(e, r), T{crr(e,r)} is constant and if e = f(t) then,
for every term algebra A,
( S
A
η∈CSubst { [[ rη ]] id | tη ⊑ s} ∪ {⊥} if h = f,
T{crr(e,r)} (A)
(s) =
h
{⊥}
otherwise
214
J. M. Molina-Bravo and E. Pimentel
Proposition 11
Let A ∈ CTAlgΣ be a consistent term algebra, and r ∈ Term⊥ . Then, for every
MRt
and TRt (A) ⊑ A. Moreover,
t ∈ [[ r ]] A
id , a program Rt exists such that t ∈ [[ r ]] id
TRt is constant.
Now, we can obtain the full abstraction property for {[ · ]}CM .
Theorem 31 (Full abstraction of {[ · ]}CM )
σ
The semantics {[ · ]}CM is fully abstract w.r.t. (Ob, {∪, (·) , (·)\σ, ρ(·)})
7 A compositional and fully abstract semantics
The fact that the consistent term model semantics is fully abstract but not compositional w.r.t. the deletion of a subsignature means that this semantics is more abstract
than necessary. We need a finer semantics but not as fine as the T-semantics. One
way of obtaining such a semantics is by increasing the number of pre-fixpoints (related to the T-operator) to be considered when we compare two programs, and to
obtain compositionality w.r.t. the deletion operation, we may consider the consistent
term models of all programs obtained by deleting a subsignature. With this idea we
define the following equivalence between programs
Definition 32 (Deletion equivalence)
For programs P and Q, we define the deletion equivalence P ≡D Q as P\σ ≡CM Q\σ
for all subsignatures σ ⊆ FSΣ .
This equivalence is finer than the consistent model equivalence and coarser than
the equivalence induced by the T-semantics. In fact, P ≡D Q implies P ≡CM Q
because this relationship coincides with P \ σ0 ≡CM Q \ σ0 , where σ0 is the empty
signature. And if P ≡T Q, or equivalently TP = TQ , it can be proved that TP\σ =
TQ\σ , for all σ ⊆ FSΣ , and then P \ σ ≡CM Q \ σ, for all σ ⊆ FSΣ , which is P ≡D Q.
The deletion equivalence is compositional w.r.t. all operations.
Theorem 33 (Compositionality of ≡D )
For all programs P, Q, Pi , Qi ,
1.
2.
3.
4.
5.
P ≡D Q implies Ob(P) = Ob(Q).
Pi ≡D Qi for i = 1, 2, implies P1 ∪ P2 ≡D Q1 ∪ Q2 .
σ
σ
P ≡D Q implies P ≡D Q , for every signature σ ⊆ FSΣ .
P ≡D Q implies P \ σ ≡D Q \ σ, for every signature σ ⊆ FSΣ .
P ≡D Q implies ρ(P) ≡D ρ(Q), for every function symbol renaming ρ.
σ
Thus, the equivalence ≡D is compositional w.r.t. (Ob, {∪, (·) , (·) \ σ, ρ(·)}).
Theorem 34 (Full abstraction of ≡D )
σ
The equivalence ≡D is fully abstract w.r.t. (Ob, {∪, (·) , (·)\σ, ρ(·)})
Definition 35 (Deletion semantics)
We define the deletion semantics {[ P ]}D , of a program P, as {Mf/n (P) | f/n ∈ FSΣ },
where Mf/n (P) is the set of all consistent term models of the rules of P that define
f/n.
The deletion semantics induces the deletion equivalence.
Composing programs in CRWL
215
Proposition 12
P ≡D Q ⇔def {[ P ]}D = {[ Q ]}D
σ
Thus, the deletion semantics is compositional and fully abstract w.r.t. (Ob,{∪, (·) ,
(·) \ σ, ρ(·)}).
8 Introducing hidden symbols
In this section we explore an alternative to modules with an infinite number of rules,
generated by the closure operation, that also supports local constructor symbols.
For this aim we will consider a global or visible signature Σ and a set V of
variable symbols together with a new set Ω of labels that we identify with the set of
module names and module expressions. With this set we obtain a labeled signature
Ω × Σ = (Ω × DSΣ , Ω × FSΣ ) which we will consider as protected or non accessible
for users and writers of modules, i.e. hidden. This signature will be only managed by
the module system for internal representation of module expressions. Pairs (M, f)
of Ω × Σ, called labeled symbols, will be denoted by M.f.
As we have seen in section 4.1 the purpose of the closure of a module is to hide
the definitions of function symbols, making only their results visible. To this aim,
the rules of a module are replaced with all (possibly infinite) approximations that
can be derived from them. However, we can obtain an internal representation of the
closure operation, with a finite number of rules, with the aid of labeled symbols,
following an idea that appears in Brogi (1993) applied to the hiding of predicate
definitions in logic programs. We go further into this idea applying it to deal with
local constructor symbols.
8.1 A finite representation of closure
Let P =< σp , σe , R > a module of PMod(Σ⊥ ) with a finite set of rules. We can
protect its rules translating them to a protected signature by labeling all function
symbols with the module’s name and introducing a bridge rule f(X) → P.f(X)
for each function symbol f/n ∈ σe . In this way we obtain a module P∗ in the
signature Σ⊥ = (DSΣ⊥ , FSΣ⊥ ∪ (Ω × FSΣ⊥ )) with an isolated (hidden) part RH , made
up of all translated rules, and a bridge part RB for accessing the isolated part,
made up of all bridge rules. Obviously with this module we can derive the same
approximations, for visible function symbols, as with P in every context. We will call
these modules structured modules to distinguish them from plain modules used up
to now. In general, a structured module will be a module < σp , σe , RV ∪ RB ∪ RH >
with a visible parameter signature σp , a visible exported signature σe , and a set of
rules with three – possibly empty – parts, a visible part RV made up of rules only
with function symbols in FSΣ , a hidden part RH made up of rules only with function
symbols in Ω × FSΣ , and a bridge part RB made up of bridge rules f(X) → P.g(X),
for any label P ∈ Ω, such that each symbol P.g has a definition rule in RH . Also,
σe is made up of all function symbols with a definition rule in RV or RB , and
σp is made up of all parameter function symbols which appear in RV . We define
216
J. M. Molina-Bravo and E. Pimentel
union, deletion of functional signature and renaming in the same way as we did in
section 4.1, but we will use deletion and renaming involving only visible signature,
and, instead of closure, we define a structured closure for a structured module
P =< σp , σe , RV ∪ RB ∪ RH > as the module P∗ =< ∅, σe , R∗B ∪ R∗H > obtained by
applying the renaming τ(P), that transforms each visible function symbol f of RV
and RB into P.f and maintains all labeled symbols, and adding new bridge rules
corresponding to the function symbols of σe . Now, we can define a representation
morphism from modular expressions made up from finite plain modules to structured
modules in the following way:
•
•
•
•
ι(P) = P, for each finite plain module P;
ι(P ∪ Q) = ι(P) ∪ ι(Q), for module expressions P and Q;
ι(P \ σ) = ι(P) \ σ, for each module expression P and visible signature σ;
ι(ρ(P)) = ρ(ι(P)), for each module expression P and visible signature renaming
ρ;
• ι(P) = (ι(P))∗ , for each module expression P.
Example 36
Let OrdList and OrdNat be the modules defined in Examples 13 and 14, respectively,
and let P be the name of the module ι(OrdNat). The representation of OrdList ∪
{isnat/1->isbasetype/1}(OrdNat) will be the structured module ι(OrdList) ∪
{isnat/1->isbasetype/1}(P∗ ), with the following aspect
<{}, {isbasetype/1,leq/2,geq/2,insert/2},
{
% visible rules
insert(X,[])
-> [X]
<= isbasetype(X) >< true.
insert(X,[Y|Ys]) -> [X|[Y|Ys]]
<= leq(X,Y) >< true.
insert(X,[Y|Ys]) -> [Y|insert(X,Ys)] <= leq(X,Y) >< false.
% bridge rules
isbasetype(X) -> P.isnat(X).
leq(X,Y) -> P.leq(X,Y).
geq(X,Y) -> P.geq(X,Y).
% hidden rules
P.isnat(zero)
-> true.
P.isnat(succ(X)) -> P.isnat(X).
P.leq(zero,zero)
-> true.
P.leq(zero,succ(X))
-> P.isnat(X).
P.leq(succ(X),zero)
-> false <= P.isnat(X) >< true.
P.leq(succ(X),succ(Y)) -> P.leq(X,Y).
P.geq(X,Y)
-> P.leq(Y,X). } >
The behaviour of a structured module P =< σp , σe , RV ∪RB ∪RH > w.r.t. the visible
signature can be expressed with the aid of the algebra transformer UP : CTAlgΣ →
CTAlgΣ defined, for each A, as UP (A) = TRV ∪RB (Tω
RH (⊥Σ ) ⊔ A)|Σ , where A is
the extension of A to an algebra of CTAlgΣ obtained by adding functions P.f A
defined as P.f A (t) = h⊥i, for each f/n ∈ FSΣ and P ∈ Ω, and B|Σ means the reduct
of the algebra B ∈ CTAlgΣ obtained by forgetting all functions denoting labeled
function symbols. In this expression, Tω
RH (⊥Σ ) represents all the information which
can be obtained from the hidden rules; this information is added to the extended
algebra because it has to be available for the immediate consequences operator
Composing programs in CRWL
217
corresponding to the visible and bridge rules, to obtain the approximations for
visible functions. The relationship, at the semantical level, between programs and
structured modules is given in the following theorem.
Theorem 37
For each modular expression E, made up from finite plain programs, and its
implementation ι(E), we have TE = Uι(E) .
From this theorem we obtain that for two equivalent module expressions P and Q
(i.e. P and Q have the same components but, possibly, different expressions with the
operations), Uι(P) = Uι(Q) although it is possible that ι(P) differs from ι(Q) due to
the occurrence of closure operations. Also, the models of a program module P will
be the pre-fixpoints of Uι(P) and we can define the visible semantics of structured
modules based on this operator. In particular we obtain the deletion semantics by
considering, for each structured module P =< σp , σe , RV ∪ RB ∪ RH >, the indexed
family of sets of pre-fixpoints of UP\(σe \f) for each f/n ∈ σe .
8.2 Local constructor symbols
To simplify the theoretical study of programs composition in CRWL-programming,
and to capture the idea of module as open program, we have assumed that constructor symbols are common to all programs. However, as it was discussed in section 4,
this assumption prevents to hide constructor symbols, what is not acceptable from
a practical point of view.
We can hide constructor symbols by labeling them as we have done with function
symbols to protect them against user manipulations. Labeled constructor symbols
can only be manipulated in the internal representation of the closure of the module
corresponding to their label. Outside this module, function symbols defined on
labeled constructor symbols can only be applied to variable symbols or to other
function applications that can be reduced to this labeled constructor symbols. To
realize this idea we only need to modify our closure implementation extending it
to manage constructor symbols also. So, we define closure hiding a subsignature
C of constructor symbols for a module P as a (non plain) module PC such that
ι(PC ) = P∗C where P∗C is obtained as P∗ but now the renaming τ(P) also transforms
each visible constructor symbol c of C into P.c.
Example 38
Let us suppose a module LNat for lists of natural numbers exporting the function
symbols isnat/1, _<_/2 and _++_/2, and consider the following module for binary
search trees where tree constructors nil/0 and mktree/3 are used.
BST =
<{isnat/1, _<_/2, _++_/2}, {empty/0, insert/2, inorder/1},
{empty -> nil .
insert(N,nil) -> mktree(N,nil,nil) <= isnat(N) >< true .
insert(N,mktree(M,T1,T2)) -> mktree(M,T1,T2) <= N >< M, isnat(N) >< true .
insert(N,mktree(M,T1,T2)) -> mktree(M,insert(N,T1),T2) <= N<M >< true .
insert(N,mktree(M,T1,T2)) -> mktree(M,T1,insert(N,T2)) <= M<N >< true .
inorder(nil) -> [] .
inorder(mktree(M,T1,T2)) -> inorder(T1)++[M|inorder(T2)] <= isnat(M) >< true .}>
218
J. M. Molina-Bravo and E. Pimentel
We may hide the tree constructors by considering (LNat ∪ BST){nil,mktree} . This module
will have the following representation:
<{}, {isnat/1, _<_/2, _++_/2, empty/0, insert/2, inorder/1},
{ ...
% bridge rules of LNat
empty -> BST.empty .
% bridge rules of BST
insert(N,T1) -> BST.insert(N,T1) .
inorder(T1) -> BST.inorder(T1) .
...
% hidden part of LNat
BST.empty -> BST.nil .
% hidden part of BST
BST.insert(N,BST.nil) -> BST.mktree(N,BST.nil,BST.nil)
<= BST.isnat(N) >< true .
BST.insert(N,BST.mktree(M,T1,T2)) -> BST.mktree(M,T1,T2)
<= N >< M, BST.isnat(N) >< true .
BST.insert(N,BST.mktree(M,T1,T2)) -> BST.mktree(M,BST.insert(N,T1),T2)
<= N NBST.< M >< true .
BST.insert(N,BST.mktree(M,T1,T2)) -> BST.mktree(M,T1,BST.insert(N,T2))
<= M MBST.< N >< true .
BST.inorder(BST.nil) -> [] .
BST.inorder(BST.mktree(M,T1,T2)) -> BST.inorder(T1) BST.++ [M|BST.inorder(T2)]
<= BST.isnat(M) >< true .}>
We can use this module, only using the exported signature and visible constructor
symbols, as is done in the following module for sorting lists:
LSort =
<{empty/0, insert/2, inorder/1},
{listTotree/1, lsort/1},
{listTotree([]) -> empty .
listTotree([N|L]) -> insert(N,listTotree(L)) .
lsort(L) -> inorder(listTotree(L)) .} >
The behavior of a structured module P =< σp , σe , RV ∪ RB ∪ RH > with hidden
constructor symbols w.r.t. the visible signature can be expressed with the aid of
the algebra transformer UP : CTAlgΣ → CTAlgΣ defined, for each A, as UP (A) =
TRV ∪RB (Tω
RH (⊥Σ ) ⊔ A)|Σ , where now A is the extension of A to an algebra of
CTAlgΩ×Σ obtained by adding functions P.f A , defined as P.f A (t) = h⊥i, for each
∗
∗
f/n ∈ FSΣ and P ∈ Ω, and defining f A (t) = f A (t ) where tuple t is obtained
from t by changing each term beginning with a labeled constructor term for ⊥, for
each f/n ∈ FSΣ , and B|Σ means the reduct of the algebra B ∈ CTAlgΩ×Σ obtained
by restricting the carrier to CTerm⊥ and forgetting all functions denoting labeled
function symbols.
Obviously, the representation of the closure w.r.t. the functional signature is a
particular case of closure hiding a set of constructor symbols when this set is empty.
9 Discussion
Research in component-based software development is currently becoming a very
active area for the logic programming community. In fact, we can find several
proposals in the field of computational logic for dealing with the design and
development of large software systems. Other related fields, like functional-logic
programming are now proving that the integration of logic variables and functions
may increase the expressive power of a programming language. A number of
Composing programs in CRWL
219
attempts are being made in this direction (Hanus, 1994, 1998) to achieve a consensus
on the characteristics a functional-logic language has to present.
The current work tries to contribute to all these efforts by presenting a notion
of module in the context of functional-logic programming, and by providing a
number of operations (satisfying some expected algebraic properties) expressive
enough to model typical modularization issues like export/import relationships,
hiding information, inheritance, and a sort of abstraction. We have chosen the
Constructor-based Conditional Rewriting Logic (González-Moreno et al., 1999) to
develop our proposal and, in this context, we have explored a rather wide range
of semantics for program modules and we have studied some of their relevant
properties, in particular, those concerning compositionality and full abstraction
σ
w.r.t. the observation function Ob(P) = MP and the set {∪, (·) , (·) \ σ, ρ(·)} of
module operations.
Although these features are interesting enough from a theoretical point of view,
they present a special significance when module reusing, module refining or module
transforming are involved. The least model semantics, {[ · ]}LM , is a fully abstract
σ
semantics, which is only compositional w.r.t. {(·) , ρ(·)}, but only for injective function
renamings ρ. On the contrary, the T-semantics, {[ · ]}T , is compositional (w.r.t. all
operations), but is not fully abstract. The third proposal, the loose model-theoretic
semantics, {[ · ]}M , is also compositional (except for the deletion operation), although
the full abstraction property is not satisfied. A fully abstract semantics, {[ · ]}CM ,
may be obtained by considering a consistency property on term algebras, which is
also compositional w.r.t. the union, closure and renaming operations. To recover
the compositionality w.r.t. deletion we need a finer semantics able to capture the
‘independent’ meaning of each function in a module; this is the case of the deletion
semantics, {[ · ]}D , which still is fully abstract and compositional w.r.t. all operations.
We have also studied the (T ⊔ Id)-semantics, {[ · ]}T ⊔I , but we have not included
this study in this paper because it exhibits the same properties as the T-semantics.
Table 1 summarizes the properties satisfied by each one of the analyzed semantics.
It is possible to establish a semantics hierarchy ranging from the model-theoretic
semantics to the T-semantics on the basis of the order ≡T ⊑ ≡T ⊔Id ⊑ ≡D ⊑ ≡CM
⊑ ≡C for the equivalence relationships induced by these semantics, where they are
ordered upon their strength. The T-equivalence relation, ≡T , is the strongest one,
and it is contained obviously into the (T ⊔ Id)-equivalence relation, ≡T ⊔Id . Taking
into account that this equivalence relation is compositional but not fully abstract,
it will be contained in ≡D , which is also contained in the consistent term-model
equivalence, ≡CM . Obviously, the least term-model equivalence, ≡LM , is the weakest
one.
To establish some conclusions about the compositionality and the full abstraction
of all these semantics, we are going to discuss the information exhibited in Table 1.
In this table, we can observe a sort of dependency between fulfilling compositionality/full abstraction and the strength of the equivalence relationship defined by
the semantics, in such a way that the strongest ones are compositional whereas
the weakest ones are fully abstract. The best semantics must be an intermediate
semantics satisfying both properties; in our case, the semantics {[ · ]}D . A similar
220
J. M. Molina-Bravo and E. Pimentel
Table 1. Compositionality (C) and full abstraction (FA)
σ
∪, (·) , (·)\σ, ρ(·)
σ
∪, (·) , ρ(·)
σ
(·) , ρ(·)
(·)
σ
{[ · ]}T
C
C
C
C
{[ · ]}T ⊔I
C
C
C
C
C
C
C
C
C
C
C
{[ · ]}D
FA
{[ · ]}CM
FA
FA
{[ · ]}LM
FA
FA
FA
FA
C
study was already made by Brogi and Turini (1995) in the field of logic programming, but he did not deal with variables, and avoided the complexity inherent to
the non-ground term algebras. Another difference (apart from the context) with
respect to the current work is the set of operations we are considering, which does
not coincide with the set of inter-module operations defined by Brogi. One of the
most significative operations described by him is the intersection of programs. This
operation makes the (T ⊔ Id)-semantics compositional and fully abstract in a logic
programming context. However, the difficult justification of this operation in our
framework (the functional-logic programming paradigm) has inclined us to think in
an alternative: the deletion operation. We believe that this operation is more natural
(as a composing mechanism) than program intersection. This has an inconvenience:
the (T ⊔ Id)-semantics is not fully abstract (although it is compositional) w.r.t. our
operations. In fact, the intersection of programs is a very powerful tool to distinguish
programs (more than the deletion operation), and it can be used to delete a single
rule, whereas our deletion operation only can be used to delete a whole set of rules
defining a function. Nevertheless, we have found a fully abstract and compositional
semantics, also for the deletion operation, which completes the results provided by
this work.
Acknowledgements
We would like to thank Mario Rodrı́guez-Artalejo and Ana Gil Luezas for their
helpful comments and suggestions on the initial versions of this work. We would
also like to thank Narciso Martı́-Oliet for their insightful comments and suggestions,
that greatly helped us improving the quality and presentation of the paper. This
work has been partially supported by the Spanish project TIC98-0445-C03-03.
References
Abramsky, S. and Jung, A. (1994) Domain theory. Handbook of Logic in Computer Science,
3, 1–168. Oxford University Press.
Apt, K. (1990) Logic programming. Handbook of Theoretical Computer Science, Vol. B: Formal
Models and Semantics, pp. 493–574. Elsevier.
Apt, K. (1996) From Logic Programming to Prolog. Prentice-Hall.
Composing programs in CRWL
221
Bergstra, J. A., Heering, J. and Klint, P. (1990) Module algebra. J. ACM, 37(2), 335–372.
Brogi, A. (1993) Program Construction in Computational Logic. PhD Thesis TD-2/93, University of Pisa-Genova-Udine.
Brogi, A., Mancarella, P., Pedreschi, D. and Turini, F. (1994) Modular Logic programming.
ACM Trans. Program. Lang. & Syst. 16(4), 1361–1398.
Brogi, A. and Turini, F. (1995) Fully abstract compositional semantics for an algebra of logic
programs. Theor. Comput. Sci. 149(2), 201–229.
Bugliesi, M., Lamma, E. and Mello, P. (1994) Modularity in logic programming. J. Logic
Program. 19(20), 443–502.
Durán, F. (1999) A Reflective Module Algebra with Applications to the Maude Language. PhD
Thesis, University of Málaga.
Ehrig, H. and Mahr, B. (1990) Fundamentals of Algebraic Specification 2. Module Specifications
and Constraints. Springer-Verlag.
Falaschi, M., Levi, G., Martelli, M. and Palamidessi, C. (1993) A model-theoretic reconstruction of the operational semantics of logic programs. Infor. & Computation, 102(1),
86–113.
Goguen, J. A. and Burstall, C. R. M. (1992) Institutions: abstract model theory for specification
and programming. J. ACM, 39(1), 95–146.
González-Moreno, J. C. (1994) Programación Lógica de Orden Superior con Combinadores.
PhD Thesis, Universidad Complutense de Madrid.
González-Moreno, J. C., Hortalá-González, M. T., López-Fraguas, F. J. and Rodrı́guezArtalejo, M. (1999) An approach for declarative programming based on a rewriting logic.
J. Logic Programl. 40(1), 47–88.
Hanus, M. (1994) The integration of functions into logic programming. A survey. J. Logic
Program. 19(20), 583–628.
Hanus, M. (ed.) (2000) Curry. An Integrated Functional Logic Language. Draft (available at:
www.informatik.uni kiel.de/˜mh/curry/report.html).
Hussmann, H. (1993) Non-determinism in Algebraic Specifications and Algebraic Programs.
Birkäuser Verlag.
Mancarella, P. and Pedreschi, D. (1988) An algebra of logic programs. In: Kowalski, R. A. and
Bowen, A. (eds.) Proc. 5th International Conference on Logic Programming, pp. 1006–1023.
MIT Press.
Meseguer, J. (1992) Conditional rewriting logic as a unified model of concurrency. Theor.
Comput. Sci. 96, 73–155.
Miller, D. (1986) A theory of modules in logic programming. Proc. of Symposium of Logic
Programming, pp. 106–114.
Molina-Bravo, J. M. and Pimentel, E. (1997) Modularity in functional-logic programming.
Proc. 14th International Conference on Logic Programming, pp. 183–197. MIT Press.
Molina-Bravo, J. M. (2000) Modularidad en Programación Lógico-Funcional de Primer Orden.
PhD Thesis, University of Málaga.
Möller, B. (1985) On the algebraic specification of infinite objects – ordered and continuous
models of algebraic types. Acta Informatica, 22, 537–578.
Orejas, F., Pino, E. and Ehrig, H. (1997) Institutions for logic programming. Theor. Comput.
Sci. 173, pp. 485–511.
Orejas, F. (1999) Structuring and modularity. In: Astesiano, E., Kreowski, H.-J. and KriegBrückner, B. (eds.), Algebraic Foundations of Systems Specification, pp. 159–200. SpringerVerlag.
Wirsing, M. (1990) Algebraic specification. Handbook of Theoretical Computer Science, Vol.
B, pp. 675–788. Elsevier.