Object-Oriented Programming in Prolog Rationale and a Case Study

Fevzi Belli^{∗}  Oliver Jack^{∗}  Lee Naish^{†}

July 1992

Abstract

Logic programming provides an opportunity to formulate and solve problems in a declarative manner. In logic programming languages, problem solving will be performed by describing what has to be done instead of describing how it has to be done as the latter is the case when utilizing procedural programming languages. The declarative way of programming offers a good method for the construction of software, e.g. for knowledge-based systems, database applications etc. because software engineers have then to be much less concerned with procedural aspects of their programs as they use a conventional programming language. Moreover, object-oriented programming as a special programming paradigm provides benefits for software engineering. In object-oriented programming languages, the relevant world to be modelled is considered as a collection of autonomous objects which encapsulate data and procedures. The objects are structured hierarchically and can inherit methods, i.e. data and procedures. This enhances the reusability and maintainability of software. Although several attempts have been made to combine both logic programming and object-oriented programming, they have not been so successful.

In this paper, we first specify logic and object-oriented programming by using a certain contamination of pure logic programming which already took place in “real” Prolog. We explain this approach by a practical example, deployed in the project FIREX (FIRe avoidance and Combat EXpertise) which is described in [1]. Then we develop another approach of objectorientation with “pure” Prolog plus negation. We also explain this approach by an example from the FIREX project.

Keywords: Predicate Logic, Logic Programming, Prolog, Object-Orientation, Knowledge-Based Expert Systems.

1 Introduction

In recent years the logic programming and object-oriented programming (OOP) have attracted a

growing interest, because both programming paradigms offer advantages over traditional program-

ming methods.

Logic programming languages like Prolog have a built-in mechanism for backtracking and pattern

matching via unification. This provides a natural way for deductive retrieval and for representation

of rule-based knowledge.

While OOP enjoys the advantages of traditional programming, considering the merits of the

modern software engineering, it provides a natural way to solve problems in which data objects can be categorized hierarchically. Data encapsulation and inheritance encourage a structured implementation style and enhance the maintainability of software. On the other hand, the deficiency of OOP is that it provides no generalized methods for deductive retrieval and pattern matching which are basic methods of knowledge-based applications. Therefore, a combination of logic programming with the OOP exploits their mutual advantages.

∗Department of Electrical and Electronics Engineering University of Paderborn Germany †Department of Computer Science University of Melbourne Australia

In this paper we describe our two different approaches to object-orientation in Prolog. The first one considers Prolog as a existing (modern) programming language, e.g. according to [4]. This is the usual way enhancing object-orientation in Prolog [28].

The programming language Prolog (as defined in [4, 6]) has many advantages concerning real life programming, provided through many constructs like univ, cut, etc. to increase the efficiency and programming comfort. However, this programming comfort has its price paid by unpleasant side effects which do not exist in “pure’ Prolog based on first-order predicate logic (FOPL). Moreover, serious tractability problems occur, when program validation aspects will be taken into account [2]. Therefore, the above mentioned built-in predicates may be enrichment from the viewpoint of programming practice, but they are risky contaminations from the theoretical viewpoint.

For this reason, our second approach to the object-orientation in Prolog considers “pure” Prolog which is Horn clause programming and has approximatly the same same expressive power and tractability as FOPL. Either approach verifies our thesis that both “real” and “pure” Prolog is compatible with the object-orientation.

Section 4 reviews our first approach: OOP with “real” Prolog. This approach depends on features of Prolog which lie outside of FOPL, forming a “contamination” of FOPL, because objectorientation is not immediately compatible with FOPL (section 4.1). We will show, however, that the above mentioned contamination of FOPL which is needed to achieve object-orientation is included in the contamination which already took place in Prolog (section 5).

The second approach of OOP with “pure” Prolog will be introduced in section 6. This approach is based on Horn clause programming including negation.

Both approaches will be depicted by examples taken from our project FIREX (FIRe avoidance and Combat EXpertise) which has been already published [1].

Finally, in section 7 we present our concluding remarks.

2 Logic programming and Prolog

Further details of the topics presented here can be found in [4, 5, 17].

2.1 Logic programming

Utilizing FOPL as a programming language is the main idea of logic programming, because a substantial subset of the FOPL has a procedural interpretation. This subset is the set of Horn clauses. Horn clauses are closed formulae of the form

∀(A), (1)

∀(A ← (B_{1} ∧ ⋯ ∧ B_{m})), (2)

¬∃(B_{1} ∧ ⋯ ∧ B_{m}). (3)

where A, B_{1}, …, B_{m} are atomic formulae, i.e. formulae of the form p(t_{1}, …, t_{n}) with predicate symbol p and terms t_{1}, …, t_{n} as arguments to p. p(X_{1}, …, X_{n}) is also called a predicate.

Horn clauses are denoted by

A ←, (4)

A ← B_{1}, …, B_{m}, (5)

← B_{1}, …, B_{m}. (6)

Clauses of the form (1), (4) are called facts, of the form (2), (5) rules and of the form (3), (6) goals or queries. In case of a rule, A is called the head and B_{1}, …, B_{m} the body of the rule.

A logic program is a finite set of facts and rules. Facts and rules are also called definite program clauses.

Example 1. Consider the following logic program which consists of facts representing the chemical/physical consistencies and properties of some substances. Rules are given for the storage of the substances as a consequence of the consistency and property facts. The consty facts describe the consistencies and the prop facts the properties.

consty(sulphur, solid) ← consty(acetylene, gas) ← consty(acetone, fluid) ←

prop(sulphur, inflammable) ←

prop(sulphur, toxic) ←

prop(acetylene, inflammable) ←

store(X, steel container) ← consty(X, solid), prop(X, toxic)

store(X, steel container) ← consty(X, solid), prop(X, inflammable) store(X, pressure tank) ← consty(X, gas), prop(X, inflammable)

⊓ ⊔

A logic program is run by assigning it an initial goal. If ← B_{1}, …, B_{m} is a goal then B_{1} is unified with the head A of an appropriate program clause A ← C_{1}, …, C_{k} (the program clause may also be a fact in case k = 0) and the goal is reduced to ← (C_{1}, …, C_{k}, B_{2}, …, B_{m})θ where θ is the unifying substitution. The process will be repeated until the empty goal (denoted by □) is reached or there are no more program clauses which heads are unifiable with the current goal. The inference rule used here is the resolution principle. In the context of logic programming we refer to SLD-resolution. SLD stands for Selective Linear Resolution for Definite Clauses. Selective (or Selected, as in [17]) means that choosing B_{i} in ← B_{1}, …, B_{m} for being unified with the head of a program clause in order to build an SLD-resolvent of ← B_{1}, …, B_{m} and A ← C_{1}, …, C_{k} is done by applying a selection rule [16, 17]. We described above SLD-resolution with the selection rule of choosing always the first atomic formula B_{1} in a goal. The resolvents are then called normal SLD-resolvents. Unification is a uniform mechanism for parameter passing, data selection and data construction. More on the terminology is given in [17].

Example 2. To activate the program given in example 1 we consider the goal

G = ← store(sulphur, X).

The computation process then involves the construction of the sequence of goals shown in figure 1.

Goal  Program Clause  Unifying

Substitution

← store(sulphur, X_{0})  store(X_{1}, steel container) ← {X_{0}/steel container,
consty(X_{1}, solid),  X_{1}/sulphur}

prop(X_{1}, toxic)

← consty(sulphur, solid), consty(sulphur, solid) ←  {}

prop(sulphur, toxic)

← prop(sulphur, toxic) prop(sulphur, toxic) ← {}

□

Figure 1: An SLD derivation

The answer to the initial goal

← store(sulphur, X)

is then yes with the computed answer

{X/steel container}.

⊓ ⊔

Such a sequence of goals constructed via unification with heads of program clauses is called a normal SLD-derivation of the initial goal G and the program P. In case the derivation ends with the empty goal □, as it does in our example, the derivation is called a normal SLD-refutation of G and P.

The process we described is nondeterministic: In the first step of the derivation of example 2 we can also use the clause

store(X, steel container) ← consty(X, solid), prop(X, inflammable).

instead of the clause

store(X, steel container) ← consty(X, solid), prop(X, toxic).

In real logic programming systems, this nondeterministic process is replaced by a deterministic process. Details can be found in [17].

2.2 Prolog

In order to compute an answer to a goal given to a program the search space is organized as a certain type of tree. The tree is called an SLD-tree.

In order to construct a refutation to a goal G and program P the program clauses in P are tried in textual order as they are arranged in P. If a derivation reaches a point where the current goal is not empty and cannot be continued because there is no program clause whose head can be unified with the goal then backtracking to the next choice point upwards the derivation takes place and the next program clause in P is tried. In case of backtracking all the unifying substitutions between the end of the derivation and the choice point are undone.

Thus a tree is constructed whose branches are derivations of G and P. This tree contains all the normal SLD-derivations of G and P. The branches in the SLD-tree which are refutations are called successful, the others failed. Figure 2 displays the SLD-tree of the program of example 1 and the goal ← store(sulphur, X).

From the above description we see that running a program P by assigning an initial goal G means to search the SLD-tree of P and G depth first left to right for successful branches. If all the branches are failed the answer to G and P is no. For details see [17].

What we briefly described so far is pure Prolog. Prolog enables a programmer to express problems and, more important, problem solving in a declarative manner leaving the task of actually generating the solution to the programming system, i.e. the Prolog system. A closer look reveals that this statement does not hold true in general. For practical reasons, the programmer has to take into account the structure of the SLD-tree which will be generated by the Prolog system from a logic program and a goal. The structure of the SLD-tree and the above mentioned search strategy for SLD-trees determines the complexity of the program execution. Most Prolog systems offer

← store(sulphur, X_{0})

[figure]

1.

← consty(sulphur, solid), prop(sulphur, toxic)

2.

← prop(sulphur, toxic)

3.

□ successful

4.

← consty(sulphur, solid), prop(sulphur, inflammable)

5.

← prop(sulphur, inflammable)

6.

□ successful

[figure]

7.

← consty(sulphur, gas), prop(sulphur, inflammable) failed

1. Program clause: store(X_{1}, steel container) ← consty(X_{1}, solid), prop(X_{1}, toxic) Unifying substitution: {X_{0}/steel container, X_{1}/sulphur}

2. Program clause: consty(sulphur, solid) ← Unifying substitution: {}

3. Program clause: prop(sulphur, toxic) ← Unifying substitution: {}

4. Program clause: store(X_{1}, steel container) ← consty(X_{1}, solid), prop(X_{1}, inflammable) Unifying substitution: {X_{0}/steel container, X_{1}/sulphur}

5. Program clause: consty(sulphur, solid) ← Unifying substitution: {}

6. Program clause: prop(sulphur, inflammable) ← Unifying substitution: {}

7. Program clause: store(X_{1}, pressure tank) ← consty(X_{1}, gas), prop(X_{1}, inflammable) Unifying substitution: {X_{0}/pressure tank, X_{1}/sulphur}

Figure 2: An SLD-tree facilities in form of built-in predicates to add non logical control information to logic programs. For convenience, this control information is syntactically denoted in form of predicates but the meaning is of procedural nature. Some of these built-in predicates are

• The cut, denoted by “!”. The cut is used to reduce the search space in the SLD-tree. It “cuts off” branches and subtrees in the SLD-tree. Thus the programmer can enhance the efficiency of the program by telling the system not to search branches of which he knows they are failed because of the program structure.

• assert(⟨clause⟩), retract(⟨clause⟩). The logic program given to the Prolog system is also called a knowledge base. The built-in predicate assert(⟨clause⟩) has the side effect of extending the knowledge base by ⟨clause⟩ which can be any program clause. Program clauses can be constructed in form of terms via unification during program execution and then be added to the knowledge base. retract(⟨clause⟩) has the side effect of deleting ⟨clause⟩ from the knowledge base. The clause deleted by retract(⟨clause⟩) is the first one which is unifiable with ⟨clause⟩. Thus a logic program, and so the SLD-tree, can be altered dynamically during execution.

• univ is an infix denoted predicate. It is denoted ⟨atomic formula⟩ =.. ⟨list⟩, and has the effect of unifying an atomic formula ⟨atomic formula⟩ with a list ⟨list⟩ in the following way: If ⟨atomic formula⟩ is p(t_{1}, …, t_{n}) then ⟨list⟩ will be unified with the list [p, t_{1}, …, t_{n}]. This also works in the opposite direction, i.e. unifying ⟨atomic formula⟩ with p(t_{1}, …, t_{n}) when given ⟨list⟩ as [p, t_{1}, …, t_{n}].

• call(⟨goal⟩) has the side effect of giving ⟨goal⟩ to the program, i.e. if ⟨goal⟩ = B_{1}, …, B_{m} then the goal ← B_{1}, …, B_{m} is activated. The combination of univ and call can then be used to handle predicate symbols as variables.

• \+(⟨goal⟩) has the effect that the SLD-tree of ←⟨goal⟩ is searched for successful branches. If a successful branch is found then \+(⟨goal⟩) fails. If the SLD-tree is finite and has no successful branches then \+(⟨goal⟩) succeeds with the computed answer {}, i.e. no substitutions for the variables in ⟨goal⟩ are made. \+ implements the negation as failure rule [17]. It should not be mixed with logical negation.

For more details of built-in predicates see [4], in which the widely used Edinburgh syntax of Prolog is introduced. From now on we refer to this Prolog notation. In the Edinburgh syntax the notation of Horn clauses is as follows:

A.  (fact),
A :- B_{1}, …, B_{m}.  (rule),
?- B_{1}, …, B_{n}.  (goal).

2.3 Prolog as a contamination of FOPL

From the last section, we can conclude the following:

Logic programming based on Prolog leads to a contamination of FOPL, i.e. Prolog is not a pure FOPL.

Among others, reasons are:

• Prolog is restricted to Horn-clauses. So we can only express positive facts, i.e. we can express what is true, but not, what is false. This is a restriction of full FOPL [17, pp. 72–74].

• Common implementations of Prolog include some built-in predicates such as cut (“!” according to [4]) which may cause that no SLD-refutation to a goal will be found although there exists one [17, pp. 63–66]. Prolog includes the predicate assert which modifies the knowledge base (i.e. the logic program) dynamically. These procedural aspects of Prolog do not exist in FOPL. Moreover, Prolog includes the predicate univ which enables the transformation of lists to predicates, and vice versa. This allows the handling of predicates as variables; also this is outside of the scope of the FOPL.

3 Object-oriented programming and Prolog

3.1 Terminology

Object-oriented programming (OOP) was first developed as a convenient approach to implement simulation problems. The main idea is that data together with functions build a unit which should be considered as the kernel of programming activities. This kernel is an abstract item, called an abstract data type. hence, we focúse on data, and not on algorithms, or functions. Accordingly, the structure of the implemented program corresponds strongly to data structures which reflect the problem solution and the chosen program design philosophy. As a cosequence, the programming task becomes more transparent. Moreover, modifications of programs can be carried out more efficiently, usually involving changes only on appropriate functions [19].

OOP views the world as a collection of objects which are characterized by several properties and a repertoire of behaviors. This paradigm mainly consists of steps to organize code and data around the objects they represent. In [19], the motto is expressed as:

“Ask not first what the system does: Ask what it does it to!”

We now introduce basic terms for object-orientation informally. Later in section 6 we follow an approach to a more formal description of object-orientation.

Definition 1 (Object). Objects are generalizations of abstract data types. An object depicts a repertoire of behaviors. A behavior of an object is determined by data and predicates on that data. The object itself consists of data describing the object and predicates on this data. ⊓ ⊔

Definition 2 (Property). Associated with an object is a set of properties. A property of an object A is a predicate p_{A}. ⊓ ⊔

In many object-oriented systems and languages properties of objects are very specific. They are deterministic in the following sense: For each property p_{A} of an object A the arguments of p_{A} are grouped in two sequences X_{1}, …, X_{i}; Y_{1}, …, Y_{j} such that for each sequence X_{1}, …, X_{i} there exists exactly one sequence Y_{1}, …, Y_{j} with p_{A}(X_{1}, …, X_{i}, Y_{1}, …, Y_{j}). Thus, the predicate p_{A} really is a function which maps the arguments X_{1}, …, X_{i} onto the values Y_{1}, …, Y_{j}, and p_{A} can be written as p_{A}(X_{1}, …, X_{i}) = (Y_{1}, …, Y_{j}). Because of this functional behavior such properties are called methods.

Definition 3 (Class, parent object). A set of objects is given a partial ordering, and the result is a hierarchical tree structure. An object A is the class of objects B and C if the set of properties of A is the intersection of the properties of B and C. Synonymously, A is called a parent object of B and C. ⊓ ⊔

The object A represents a generalization of several objects. In many object-oriented systems A is called the father class of B and C.

Definition 4 (Superclass, subclass, root class). The terms superclass and subclass are defined recursively: A is a superclass of B if either A is the parent object of B or there exists a class C which is the parent object of B and A is a superclass of C. B is a subclass of A if either A is a parent object of B or there exists a class C of which A is a parent object and B is a subclass of C. A class which has no superclass is called a root class. ⊓ ⊔

Definition 5 (Inheritance). A class inherits by default the properties of its superclasses. These default properties can be altered. ⊓ ⊔

Even properties of an object which are inherited and not altered may have a different behavior than in the superclass because they may depend on other altered properties. Inheritance affects substantially the class hierarchy, because subclasses are generated from existing classes by means of inheriting properties and adding some new properties. Properties may also be redefined. Redefinition of a property can be seen as overriding defaults.

Definition 1 leads to a tree structure on the sets of objects. Some OOP systems and languages allow the partial order of the set of objects to be a lattice, i.e. an object may have more than one parent object. This results in a principle called multiple inheritance.

Definition 6 (Message). The objects are activated through stimuli from other objects. An object name thogether with a property is called a message. ⊓ ⊔

Via messages, object communicate with each other. A property of an object can contain a message, and thus is defined by a property of another object.

Definition 7 (Information hiding, data encapsulation). The way the properties are implemented is not transparent to the outside world of an object and properties are only accessible via objects. This principle is called information hiding as well as data encapsulation. ⊓ ⊔

More details of the concept of OOP can be found in [7, 21, 23, 25, 27, 29, 30, 33]. There are various implementations of programming languages which provide partial or full object-orientation. Examples are SIMULA [8] and Smalltalk [13].

3.2 Shortcomings of pure OOP

The merits of OOP, especially data encapsulation and inheritance as mentioned above, support structured design and enhance maintainability of large software systems. However object-orientation lacks built-in functions which are contained in most knowledge-based applications, e.g. generalized facilities for deductive retrieval and pattern matching [15]. On the other hand, Prolog has built-in deductive retrieval through backtracking and pattern matching via unification. Thus, coupling both paradigms (i.e. object-oriented and logic programming) would allow their mutual advantages. There exist some extensions of Prolog for OOP, e.g. AMORE [22], SPOOL [12], and ALF (Alltalk Logic Facility) [18]. Another approach to OOP in Prolog is presented in [35]. Also extensions of parallel logic programming languages for OOP have been developed, e.g. Polka [9], Parlog++ [10] for Parlog [3]. Another approach can be found in [26].

To integrate the logic- and OOP, we could alternatively choose from following:

• implementing object-orientation as an add-on to Prolog,

• implementing a Prolog-like deductive retrieval mechanism in an object-oriented environment [18], and

• building a bridge between two existing systems based on OOP and logic programming [15].

We investigate here the first possibility, i.e. implementing object-orientation as an add-on to Prolog.

4 Object-oriented programming with real Prolog

To implement the object-orientation as an add-on to Prolog, objects will be represented as predicates of a Prolog program. Data of an object are treated as arguments. This also provides a natural materialization of the information hiding principle. A number of program clauses heads of which are atoms of the same predicate symbol materialize a Prolog data capsule.

Properties of objects are realized here in the sense of methods (see section 3.1). Therefore, in this section, we refer to properties of objects by the term method. Methods are realized as the body of program clauses. The arguments in the head of the program clause constitute selectors in order to choose as the adequate method.

4.1 Formulation of OOP in real Prolog

PROLoop (PROlog-based Language for object oriented programming) [28] is an add-on to Prolog. This means that the base of PROLoop is logic programming with Prolog. All features of the object-orientation, e.g. objects, methods, and inheritance, are described in Prolog clause form. This clause form is not the pure logical clause form, moreover essential parts of PROLoop utilize Prolog predicates with side effects. Side effect predicates are especially used for the generation of objects and for the realization of the inheritance principle of OOP.

PROLoop has its own data base consisting of data capsules depicted in Prolog predicate definitions. For a Prolog program P and a predicate symbol r we call the set of all program clauses in P which have r in their head the definition of the predicate r. In PROLoop we relate to side effect predicates, i.e. to predicates which have a procedural meaning, thus it is justified in this context to call the definition of a predicate also a procedure definition.

A data capsule in PROLoop consists of three elements:

1. The connection to its parent class

2. The data

3. The methods

The connection of an object to its parent class is expressed by a fact

⟨object⟩(a kind of, ⟨object⟩). (7)

Facts of the form (7) define the names of objects and the class relation according to definitions 1 and 3 in section 3.1. Sub- and superclasses with respect to definition 4 in section 3.1 are given by the transitive closure the system generates from the class relation defined by facts of the form (7).

The data of an object consists of variables and their values. It is represented by a fact

⟨object⟩(a kind of, ⟨object⟩). (8)

A method of an object is a Prolog program of the following structure:

⟨object⟩(⟨method⟩, SELF , RESULT ):-⟨goal⟩. (9)

⟨goal⟩ can be any body of a Prolog program. Facts of the form (8) and rules of the form (9) realize the properties of objects according to definition 2 in section 3.1. The properties are decomposed in a data part and a method part because of the above mentioned functional realization. Therefore methods are not backtrackable in this approach. The system realizes inheritance in accordance to definition 5 in section 3.1 via the transitive closure of the class relation defined by facts of the form (7).

In general a method returns a result, i.e. some term is returned by a method. This resulting term must be unified with the variable RESULT . Programmers are responsible for implementing it. However, the implementation of a method results in an outcome without a resulting term. If the programmer does not refer to the variable RESULT then RESULT will not be substituted. Methods not returning a resulting term are used for writing data to a file, print some message on the screen etc. Such methods only invoke procedural built-in predicates of Prolog. A resulting term must not be provided by a method but if a resulting term should be returned it has to be unified with the variable RESULT .

Messages according to definition 6 in section 3.1 are given by a goal of the form

?-X<-⟨object⟩:⟨method⟩. (10)

Here X is the variable which will be bound to the resulting term of the method.

4.2 Internal representation

PROLoop data capsules are generated by a Prolog predicate which uses the assert built-in predicate of Prolog systems. This assures that only complete data capsules can be generated, i.e. data capsules which contain all the three parts.

PROLoop has a built-in predicate

     define_object(OBJECT, FATHERCLASS,
                   VARIABLES, METHODS)

(11)

which creates a data capsule

object(a_kind_of, FATHERCLASS).
object(variable_1, value_1).
object(variable_2, value_2).
            ~
object(variable_n, value_n).
object(method_name_1, SELF, RESULT) :-
      <implementation of method 1>.
object(method_name_2, SELF, RESULT) :-
      <implementation of method 2>.
            ~
object(method_name_m, SELF, RESULT) :-
      <implementation of method m>.

(12)

This enables, together with the message realization (10), data encapsulation according to definition 7 in section 3.1.

The definition of a method for an object may depend on other methods of the same object. To indicate this, an additional parameter is assigned to the predicate, which defines the method. This parameter is called SELF. The variable SELF indicates the actual object, i.e. the object for which the method is activated.

Example 3. Defining an object room and a method rent for room, the data capsule would be

%  a typical room
room(a_kind_of, object).
room(square_meters, 30).  % square_meters with default 30
room(price_per_sq_m, 20)  % price_per_sq_m with default 20
room(rent, SELF, RESULT) :-
      SQ <- SELF:get(square_meters),
      PSQ <- SELF:get(price_per_sq_m),
      RESULT is SQ * PSQ.

The method rent is then inherited by the object small room. But small room has other values for the variables square meters and price per sq m. The data capsule for small room contains

small_room(square_meters,25).
small_room(price_per_sq_m,12).

The activation of the method rent for small room leads to the calculation of the rent determined with the values for small room instead of the values for room, i.e.

RENT <- room:rent

outcomes RENT = 600 and

RENT <- small_room:rent

outcomes RENT = 300. ⊓ ⊔

The definition of an object is given in (11). Thus, every object is to be associated with a parent class. Therefore, PROLoop contains a built-in class object which represents the root class of any PROLoop program.

Methods are activated through messages realizing the communication between objects. In order to pass a message to the receiver object, the method’s name and a variable which shall contain the result must be specified. Prior to further operations, the receiver itself is checked whether or not an appropriate method has explicitly been defined. If the required method is not part of the receiver object, the object hierarchy will be searched upwards to locate the object for which the method was explicitly defined. Recall that the first element object(a kind of, FATHERCLASS) in a PROLoop data capsule refers to its parent class. Searching the object hierarchy upwards, one has to convert the variable FATHERCLASS into a predicate symbol in order to find the parent class of FATHERCLASS. This is implemented by treating Prolog variables as predicates; this treatment takes place by the univ predicate following the call predicate.

By applying univ and call, a substantial feature of the second-order predicate logic is used. This feature is the ability of having variables for predicate symbols. To separate reasoning about the object hierarchy, i.e. finding objects in the hierarchy, amongst other implications in the knowledge base, a special operator a kind of is used, as we introduced to materialize the connection of the data capsule to its parent class.

5 Case study: FIREX project

As a demonstration of OOP with PROLoop, we present an example from the FIREX system (FIRe avoidance and combat EXpertise), a knowledge-based system to deal with problems of maritime transportation of dangerous goods and to make recommendations for fire department of a small German city [1]. In this section, we describe the objectives of FIREX and its structure with respect to the problems it is supposed to solve.

5.1 The objectives of FIREX

Nowadays, there exist a great number of computer supported data bank systems to deal with the problems concerning transport and stowage of dangerous goods, and protecting human health and environment in case of fire. Examples for German and international data bank systems are INFUCHS [34], CHEMDATA [20], or EXIS [11]. These systems mainly provide information about critical features of dangerous substances and explain how they should be treated. Well structured, international systems identify and classify dangerous substances uniquely, e.g. IMDG (International Maritime Dangerous Goods) Code and UN numbers [14, 24].

The above mentioned data bank systems provide the user with information usually on one single substance. An expert system is supposed to achieve specific information on several substances; especially when one has to make an optimal decision concerning interaction of such substances. The objective is the reduction of the risk when dangerous goods are to be handled. As an example, FIREX addresses to the question: How should they be stowed onto or into a ship? Apart from minimizing the risk of damage of the shipment as well as the environment, also the cost of the transport, stowage and storage need to be minimized. Moreover, one must take the worst case into account: What should be done when something goes wrong, e.g. leakage, fire on board etc? What is the best method to minimize the damage on the shipment, and especially on human health and ecological environment, during the combat against the undesired events of the worst case? Roughly spoken, FIREX tries to simulate the activities of an human expert in this field.

For the construction of an appropriate system about the transport of dangerous goods and fire combat, knowledge and methods are necessary in the following areas:

• Environmental protection. Dangerous substances and their physical and chemical reactions, fire protection.

• Methods of Computer Science, especially data and knowledge based systems.

• Operations research to determine the optimal decision.

The concept of the knowledge based system FIREX (FIRe avoidance and combat EXpertise) considers following potential applications:

Decision support. Carrying and stowage of dangerous goods must be well planned and carried out. We mentioned above some of the interesting optimization problems concerning minimizing the risk subject to the costs.

Training and tutoring. For the fire brigade, the training of the staff prevents a difficult problem. On one hand, the potential team leaders must be trained thoroughly, best on the job. On the other hand, the realistic simulation of fire combatting theaters is expensive and risky. FIREX is supposed to be employed here, mostly to evaluate the trainee’s suggestions to plan and control the fire combat actions.

Fire combat. After a successful training, the FIREX is supposed to be deployed in practice.

Figure 3 shows the overall structure of the FIREX System.

5.2 Problems with dangerous goods

Dangerous goods (we will synonymously use dangerous substances) are those which are inherently dangerous as well as those which demonstrate chemical reaction with each other. The latent hazard [figure]

Transport Experts

S t o w a g e P r o p o s a l

[figure] [figure]

Fire Brigade Executive Officer

E x t i n g u i s h i n g P r o p o s a l

Consultation knowledge on:

• load description

• description of the relevant environment

[figure]

Experts for Dangerous Goods

Consultation knowledge on:

• legal regulations

• optimization algorithms

Interface Expertise & Explanation Component

Optimization Algorithms

Knowledge Base (Substance features/ legal rules)

[figure] [figure]

Existing data bank system on Dangerous Goods (e.g. INFUCHS)

Figure 3: The structure of the FIREX system

entails the need for segregation between different substances. Fire may spread more rapidly if

inflammable substances are not effectively separated from ignitive ones. Inflammable substances

should be segregated from those which may explode in case of fire.

5.3 Transport of dangerous goods — general remarks

In shipping, four general segregation distances have been defined considering their potential hazard

as well as the construction of the storages. These distances are abbreviated and referred to as

following:

1: away from,

2: separated from

3: separated by complete compartment from

4: separated longitudinally by an intervening complete compartment from.

(Compartment means enclosed space of which boundaries should be resistant to fire and water.)

In addition to these distances, we use several terms in the optimization procedure.

#: not known 0: no segregation recommended

5: prohibited (substances involved cannot be shipped together)

For the shipment of goods, nine classes have been introduced as a guideline for the first and rough assessment of properties of any dangerous substance. The classes were further subdivided into subclasses. The table containing segregation distances between subclasses (classes) is published in most guides to storage and stowage of dangerous goods [24, 31].

In addition to a segregation, there may be a need to segregate a particular substance from materials contributing to its hazard. It refers to the whole class or sometimes to the particular type of substances. Properties of some substances may be considered different with respect to their flash point.

For the sake of fulfilling any requirement each dangerous substance is described on an individual sheet in IMDG (International Maritime Dangerous Goods) Code Book [14, 31].

To maintain proper distance one should select the worst case from the segregation distances with respect to:

• classes segregation table

• special segregation with the selected classes

• special segregation with the selected types

• segregation requirement depending on flash point limits.

Classes and limits of flash point are well defined for every substance. Proper assignment of the substance to any type appears to be more complicated and sometimes expert knowledge is needed.

5.4 OOP in FIREX

We now turn to the object-orientation in real Prolog using FIREX as a non-trivial environment. We concentrate on the problem of segregation of dangerous substances and refer to the official guidelines for the storage and stowage of dangerous goods [14, 31] as mentioned in section 5.3. The example depicts a simplified program of FIREX materializing the introduced global expertise. We define danger categories and substances as classes in the sense of object-orientation. A method of the class substance is implemented to determine the segregation rules when substances are given. Object classes having substances as subclasses view the segregation rules for substances as variables. These rules correspond to the substance/danger category concerned and its exceptions.

The PROLoop program consists of class definitions including method definitions. Through class definitions the programmer maps the class hierarchy and declares class variables and assigns default values. According to the hierarchy, methods and default values are inherited. Danger categories like explosive, inflammable solid, poison, corrosive, etc. and also substances which belong to these danger classes are declared first. Please note that some danger classes may have subclasses, e.g. danger class 2 (gases) has two subclasses: 2.1 (inflammable) and 2.2 (non inflammable).

Example 4. Part of PROLoop code from FIREX system.

/* Definition of some sample classes
   ("object" is always root class) */
:- define_object(substance, object, [rules:=[nil]],
      [
       method(segregate(S), SELF, RESULT,
          (
             (
              LIST <- SELF:get(rules),
                 subsequence([S, RESULT], LIST)
             )
          ;
             true
          )
      ]
             )). /* end of definition of substance */
:- define_object('class 2', substance, [], []).
:- define_object('class 4', substance, [], []).
:- define_object('class 2.1', 'class 2',
     [rules:=[[acetylene, 0],
              [chlorine_cyanide, 0],
              [liquid_air, 0],
              [sulphur, 1],
              [dinitrophenole, 1]]],
     []).
:- define_object('class 4.1', 'class 4',

[rules:=[[sulphur, 0],
[dinitrophenole, 0],
[acetylene, 1],
[chlorine_cyanide, 1],
[liquid_air, 1]]],
[]).
:- define_object(acetylene, 'class 2.1',
[rules:=[[acetylene, 0],
[chlorine_cyanide, 0],
[liquid_air, 2],
[sulphur, 1],
[dinitrophenole, 1]]],
[]).
:- define_object(liquid_air, 'class 2.1',
[rules:=[[acetylene, 2],
[chlorine_cyanide, 0],
[liquid_air, 0],
[sulphur, 1],
[dinitrophenole, 1]]],
[]).
:- define_object(chlorine_cyanide, 'class 2.1', [], []).
:- define_object(sulphur, 'class 4.1', [], []).
:- define_object(dinitrophenole, 'class 4.1', [], []).

⊓ ⊔

This PROLoop program allows to inspect the segregation rule for two dangerous substance, i.e.

• away from,

• separated from etc.

As an example, now one can ask the system:

?- RESULT <- acetylene:segregate(liquid_air).

     RESULT = 2
     yes

The method segregate is defined for the class substance and inherited downwards by the classes 'class 2' and 'class 4'. The method is then further inherited downwards by the classes 'class 2.1', 'class 4.1' and so on. Thus, when giving the goal

     ?- RESULT <- acetylene:segregate(liquid_air)

to the system the object hierarchy has to be searched upwards for the definition of the method segregate (this method is available for all objects but has been explicitly defined only for the root object). For this purpose, due to the structure of the data capsules of PROLoop (see (11) and (12) on page 13) several invocations of the univ and call built-in predicates of Prolog take place. We thereby handle variables (the variables which represent the parent classes of the objects) as predicates, thus utilize a feature of second-order predicate logic.

6 OOP in pure Prolog

This section constitutes another approach of combining Prolog and OOP. This approach is based on pure Prolog and uses only Horn clause programming and negation. According to section 3.1, we map the terminology of object-orientation into a representation using Horn clauses and negation.

6.1 Definition of the semantics of inheritance

As defined in section 3.1, an object is an arbitrary item which possesses a finite set of properties. The clause representation of an object according to definitions 1 and 2 in section 3.1 is

object(⟨object⟩) ← (13)

isrule(⟨object⟩, rule(⟨property⟩, ⟨goal⟩)) ← (14)

where ⟨object⟩ is a constant denoting the object name, ⟨property⟩ is a term which function symbol denotes the property name, and ⟨goal⟩ is a term of the form

• true

• prop(⟨object⟩, ⟨property⟩)

• and(⟨goal⟩, ⟨goal⟩). Clause (13) defines the name of an object and clauses of the form (14) define properties of an object. Classes, subclasses and superclasses together with inheritance according to definitions 3, 4, and 5 in section 3.1 are represented by the following clauses:

isa(⟨object⟩, ⟨object⟩) ← (15)

interpr(prop(self, Prop), Self) ← getprop(Prop, Self, Self)  (16)
interpr(prop(Obj, Prop), Self) ← object(Obj), ¬(Obj = self),  (17)

getprop(Prop, Obj, Self)

interpr(and(A, B), Self) ← interpr(A, Self), interpr(B, Self)  (18)
interpr(true, Self) ←  (19)
getprop(Prop, Obj, Self) ← isrule(Obj, rule(Prop, Goal)),  (20)

interpr(Goal, Self)

getprop(Prop, Obj, Self) ← ∀ Goal ¬isrule(Obj, rule(Prop, Goal)),  (21)

isa(Obj, Parent), getprop(Prop, Parent, Self)

On the set of objects we have a relation called isa. For two objects A and B, isa(A, B) means that B is a generalisation of object A, or A is a specialisation of B. This is the class or parent object relation from definition 3 in section 3.1.

The isa relation is restricted to define a tree.

The isa relation is given by clauses of form (15), and clauses (20) and (21) realize inheritance by propagating the isa relation to the object closest to the chosen one defining the chosen property. This is done by the use of negation in clause (21).

Messages according to definition 6 in section 3.1 are realized by clauses (16) and (17). A term ⟨goal⟩ of the form prop(⟨object⟩, ⟨property⟩) in a definition of a property of an object by a clause of the form (14) is handled by the clauses (16) and (17) such that these are true for the values of ⟨property⟩ of ⟨object⟩.

The definition of a property of an object may depend on other properties of the same object. In order to realize this to work in case of inheritance, a special object denotation called self is introduced which denotes the actual object. The self mechanism is also handled by clauses (16) and (17). Furthermore an additional argument is needed in all interpr clauses ((16)–(19)). This clause representation realizes only weak data encapsulation according to definition 7 in section 3.1 in the sense that properties are accessible only via objects. In the next section a practical implementation using a preprocessor is described. A transformation has been chosen in the practical implementation because of efficiency considerations. Using a preprocessor enables a better syntax and enhances data encapsulation.

How the clause representation of object-orientation works is best seen by an example.

Example 5. Consider two objects desk and mydesk where mydesk is a specific desk. The object desk has the properties dnum(N) which denotes the number of drawers, dlen(L) which denotes the length of the drawers in cm, and dwidth(W) which denotes the width of the drawers in cm. The default values for desk are

• dnum(3),

• dlen(70),

• dwidth(30).

Furthermore the object desk has a property dtotarea(R) denoting the total drawer area in cm^{2} which is defined by

dtotarea(R) := dnum(N) · dlen(L) · dwidth(W).

Hence the value of dtotarea(R) of the object desk is dtotarea(6300) (= 3·70·30). The object mydesk inherits all the properties of desk but the number of drawers is redefined. The value of dnum(N) of the object desk is dnum(4), and thus the value dtotarea(R) of mydesk is dtotarea(8400) (= 4 · 70 · 30).

For the definition of the property dtotarea(R) a property mult(X, Y, Z) is needed which gives in Z the product of X and Y. mult(X, Y, Z) is defined as a property of an object named top which is the parent object of desk. This is the pure oop-way to define it. mult could also be treated as a built-in defined by call in the meta interpreter.

In the following clause representation the clauses defining the top object and its property mult(X, Y, Z) are omitted.

object(desk) ← (22)

isa(desk, top) ← (23)

isrule(desk, rule(dnum(3), true)) ← (24)

isrule(desk, rule(dlen(70), true)) ← (25)

isrule(desk, rule(dwidth(30), true)) ← (26)

isrule(desk, rule(dtotarea(R), and(prop(self, dnum(N)), and(prop(self, dlen(L)),

and(prop(self, dwidth(W)),  (27)

and(prop(top, mult(N, L, X)), prop(top, mult(W, X, R)))))))) ← object(mydesk) ← (28)

isa(mydesk, desk) ← (29)

isrule(mydesk, rule(dnum(4), true)) ← (30)

Now let clauses (22)–(30) together with the clauses of the top object be P, and let clauses (16)–(21) be S. Then from P ∪ S follows

interpr(prop(desk, dtotarea(6300)), desk)

and

interpr(prop(mydesk, dtotarea(8400)), mydesk)

as desired.

⊓ ⊔

It is straightforward to translate the above clauses into a more “Prologish” form. The Prolog form of the interpreter will be given in NU-Prolog [32]. NU-Prolog includes a sound implementation of inequality, denoted ~=, a constructive if-then-else, and existential quantification of variables, denoted by some.

Clauses (16)–(21) correspond to a meta interpreter. The function symbol prop is replaced by a special predicate ::/2 declared as an infix operator to encode the a property of an object by

is rule(⟨object⟩, (⟨property⟩:-⟨goal⟩)). (31)

The function symbol rule is replaced by an infix-operator :- as usual in Prolog. Besides this also “normal” Prolog definitions of predicates can be given and called in ⟨goal⟩. Figure 4 shows the meta interpreter.

6.2 Practical implementation

The meta interpreter described in the last section defines object-orientation in pure Prolog including negation. For the real system to use we developed a preprocessor and a runtime system to overcome the meta interpreter overhead. This also enables a better syntax. Instead of encoding the properties of an object by

is rule(⟨object⟩, (⟨property⟩:-⟨goal⟩)),

as given in (31), all properties of an object are encoded by

interpr((A, B), Self) :-
     interpr(A, Self),
     interpr(B, Self).
interpr(Obj::Prop, Self) :-
     (if Obj = self
      then O = Self
      else object(Obj),
           O = Obj
     ),
     get_prop(Prop, O, Self).
interpr(Goal, S) :-
     Goal ~= (_, _),
     Goal ~= (_::_),
     call(Goal).
get_prop(Prop, O, Self) :-
     functor(Prop, F, N),
     if some[Prop1, Goal]
         (functor(Prop1, F, N),
          is_rule(O, (Prop1 :- Goal)))
      then
             Prop1 = Prop,
             interpr(Goal, Self)
      else
             is_a(O, OParent),
             get_prop(Prop, OParent, Self).

Figure 4: The meta interpreter for OOP with Prolog

def object ⟨object⟩ [is a ⟨object⟩].

⟨property⟩ :- ⟨goal⟩,

⋮

⟨property⟩ :- ⟨goal⟩.

end def ⟨object⟩.

(32)

The preprocessor translates such object definitions into Horn clauses including negation

(provided that ⟨goal⟩ contains only pure Prolog including negation and literals of the form

⟨object⟩::⟨property⟩).

The transformation of such a object definition is as follows:

isa(⟨object⟩, ⟨object⟩).

object(⟨object⟩).

defines prop(⟨object⟩, ⟨property1⟩).

⋮

defines prop(⟨object⟩, ⟨propertyN⟩). ⟨object⟩(⟨property1⟩, Self) :- ⟨goals1⟩. ⟨object⟩(⟨propertyN⟩, Self) :- ⟨goalsN⟩.

call object(⟨object⟩, ⟨property1⟩, Self) :-

⟨object⟩(⟨property1⟩, Self).

⋮

call object(⟨object⟩, ⟨propertyN⟩, Self) :-

⟨object⟩(⟨propertyN⟩, Self).

The clauses defines prop(⟨object⟩, ⟨property⟩) are used to find the object explicitly defining the (chosen) property. Clauses

call object(⟨object⟩, ⟨property⟩, Self) :-

⟨object⟩(⟨property⟩, Self)

are used for efficiency reasons. This avoids the use of Prolog’s call built-in predicate.

Example 6. The object desk from example 5 will be defined, as follows:

def_object desk.
        % default vaules for number, size of drawers
d_len(70).      % drawer length
d_num(3).       % drawer number
d_width(30).    % drawer width
        % drawer total area - general rule
d_tot_area(R) :-
        self::d_num(Num),
        self::d_len(Len),
        self::d_width(Width),
        R is Num*Len*Width.
end_def desk.

Then, the transformation into pure Prolog results in

isa(desk,'OBJECT').
object(desk).

defines_prop(desk,d_len(A)).
defines_prop(desk,d_num(A)).
defines_prop(desk,d_width(A)).
desk(d_len(70),Self).
desk(d_num(3),Self).
desk(d_width(30),Self).
desk(d_tot_area(A),Self) :-
        Self::d_num(C),
        Self::d_len(D),
        Self::d_width(E),
        A is C*D*E.
defines_prop(desk,d_tot_area(A)).
call_object(desk,d_len(A),Self) :-
        desk(d_len(A),Self).
call_object(desk,d_num(A),Self) :-
        desk(d_num(A),Self).
call_object(desk,d_width(A),Self) :-
        desk(d_width(A),Self).
call_object(desk,d_tot_area(A),Self) :-
        desk(d_tot_area(A),Self).

The runtime system mainly realizes the get prop(⟨property⟩, ⟨object⟩, Self) predicate of the meta interpreter. This also enhances data encapsulation (see definition 7 in 3.1). Furthermore, we have implemented a top level object called ′OBJECT′ which contains some useful properties. If in the definition of an object the is_a part is missing, then the parent object will be the top level object. One of the properties of the top level object is parent(P), which gives for the variable P the parent object. The syntax of (32) and the top level object assure, together with a mechanism of detecting cycles, a tree structure of the object hierarchy.

6.3 Example in OOP in pure Prolog, taken from the case study FIREX

As in section 5.4, we concentrate on the problem of segregation of dangerous substances and refer to the official guidelines for the storage and stowage of dangerous goods [14, 31]. The example covers the following substances and classes:

• subclass class 2.1 of class 2 with the substances

– acetylene

– liquid air

– clorine cyanide

• subclass class 4.1 of class 4 with the substances

– sulphur

– dinitrophenole.

The general segregation distances are

0 between substances of class 2.1

0 between substances of class 4.1

1 between substances of class 2.1 and class 4.1.

For the definition of these segregation distances see section 5.3. In addition, there is an exception. Between the substances liquid air and acetylene (both in class 2.1), the segregation distance is 2 instead of 0.

This leads to an interesting problem: the inheritance here is not only on the property level but also on the level of particular arguments. Thus, only part of the property definitions should be overwritten. With the above described oop extension to Prolog this can be done. In the implementation, we first define an object substance including a property seg(Subst, Rule). This object includes also a property segregate(Subst, Rule) giving a default of segregation 0. All subsequent objects may override the segregate(Subst, Rule) property. The seg(Subst, Rule) property then realizes the inheritance on the argument level.

     def_object substance.
             % find segregation rule for "self" and Subst
             % (both substances or classes of substances at the same level
             % in the class hierarchy)
     seg(Subst, Rule) :-
             ( if self::segregate(Subst, R)  % if we have a specific fact
             then
                     Rule = R                % then use it
             else                            % otherwise

                Subst::parent(P),       % consider the parent objects
                self::parent(SP),       % (classes of substances)
                SP::seg(P, Rule)        % and find their
                                        % segregation rule
        ).
        % default segregation between a substance
        % and any other substance is 0
segregate(_, 0).
end_def substance.

This object has two subclases 'class 2.1' and 'class 4.1'. All subclasses of substance will contain a property segregate(Subst, Rule) which defines the segregation distances (rules).

def_object 'class 2.1' is_a substance.
segregate('class 2.1', 0).
segregate('class 4.1', 1).
end_def 'class 2.1'.
def_object 'class 4.1' is_a substance.
segregate('class 2.1', 1).
segregate('class 4.1', 0).
end_def 'class 4.1'.
Now come the substances acetylene, liquid air, clorine cyanide, sulphur, and
dinitrophenole, all inheriting the properties seg(Subst, Rule) and segregate(Subst, Rule):
def_object acetylene is_a 'class 2.1'.
segregate(liquid_air, 2).
end_def acetylene.

def_object liquid_air is_a 'class 2.1'.
segregate(acetylene, 2).
end_def liquid_air.

def_object clorine_cyanide is_a 'class 2.1'.
end_def clorine_cyanide.

def_object sulphur is_a 'class 4.1'.
end_def sulphur.

def_object dinitrophenole is_a 'class 4.1'.
end_def dinitrophenole.
For the objects acetylene and liquid_air the property segregate(Subst, Rule) has been rede-

fined. Because in segregate(Subst, Rule) are only the exceptions encoded, the relevant property for the segregation distances is seg(Subst, Rule).

This program is very flexible in the sense of getting information about segregation distances, as the following examples illuminate.

Example 7. To get information about the substances and segregation distances for sulphur, type in the goal

?- sulphur::seg(Substance, Distance).

The answers are

Substance = 'class 2.1',
Distance = 1 ;

Substance = 'class 4.1',
Distance = 0 ;
Substance = acetylene,
Distance = 1 ;
Substance = liquid_air,
Distance = 1 ;
Substance = clorine_cyanide,
Distance = 1 ;
Substance = sulphur,
Distance = 0 ;
Substance = dinitrophenole,
Distance = 0

⊓ ⊔

Example 8. This also works in the “opposite” direction:

?- Substance::seg(acetylene, Distance).

The answers are

Substance = acetylene,
Distance = 0 ;
Substance = liquid_air,
Distance = 2 ;
Substance = clorine_cyanide,
Distance = 0 ;
Substance = sulphur,
Distance = 1 ;
Substance = dinitrophenole,

     Distance = 1

Here one can see the exception for liquid air and acetylene. ⊓ ⊔

Example 9. Finally, one gets all substances to be segregated by segregation distance 2 with the goal

     ?- Substance1::seg(Substance2, 2).

The answers are

     Substance1 = acetylene,
     Substance2 = liquid_air ;
     Substance1 = liquid_air,
     Substance2 = acetylene

⊓ ⊔

Of course, all information about segregation distances can be obtained at once with the goal

     ?- Substance1::seg(Substance2, Distance).

We do not give the list of the answers here because it is rather long, even this is a very small example.

7 Conclusions

The “marriage” of logic programming and object-oriented programming provides the programmer with the benefit of having expressive power of as well declarative as structural/procedural representation of problems and problem solving. At the same time, this marriage on the basis of logic programming achieves a uniform and convenient programming environment. We presented two approaches, one based on “real” Prolog which heavily uses features of Prolog outside logic programming (dynamic modifications of the database), and one based on Horn clause programming plus negation. Although both approaches have their advantages, the second one has well defined semantics.

Further research has to be done on the aspect of genericity, i.e. the dynamic generation of objects and on the modification/extension of the both models of object-orientation incorporating multiple inheritance.

References

[1] F. Belli, H. Bonin, H. Gerdes, W. Filipowicz, and P. Jedrzejowicz. Some aspects on the development and validation of FIREX — a knowledge-based system for the transport of dangerous goods and fire department consulting. In Proceedings 2nd Intern. Conference of IEA/AIE-89, pages 680–689, June 1989.

[2] Fevzi Belli and Oliver Jack. PROTest II: Testing logic programs to validate rule-based expert systems. To appear in: Proc. 2nd International Conf. on Software Quality, Research Triangle Park, N.C., Oct 1992.

[3] K.L. Clark and S. Gregory. PARLOG: Parallel programming in logic. ACM Transactions on Programming Languages and Systems, 8(1):1–49, 1986.

[4] W.F. Clocksin and C.S. Mellish. Programming in Prolog. Springer Verlag, Berlin Heidelberg, 1987.

[5] J. Cohen. A view of the origins and development of Prolog. Communications of the ACM, 31(1):26–36, 1988.

[6] A. Colmerauer. Une introduction á Prolog III. Journée de Synthèse AFECT, Etat de l’ Art et Perspectives en Programmation Language, INRIA, pages 129–155, 1989.

[7] Brad J. Cox. Object Oriented Programming — An Evolutionary Approach. Addison Wesley, 1986.

[8] O.J. Dahl, B. Myhrhaug, and K. Nygaard. SIMULA 67 — Common Base Language. Technical report, Norwegian Computing Center, 1968.

[9] Andrew Davison. Polka: A Parlog Object Oriented Language. PhD thesis, Imperial College, Dept. of Computer Science, London, September 1989.

[10] Andrew Davison. From Parlog to Polka in two easy steps. In PLILP’91: 3rd International Symposium on Programming Language Implementation and Logic Programming, pages 171–182. Springer Verlag, 1991. LNCS 528.

[11] EXIS, Chemical and Hazardous Materials Information and Transport Regulations Database. Exis Ltd., London.

[12] Koichi Fukunaga and Shin-ishi Hirose. An experience with a Prolog-based object-oriented language. In Proc. OOPSLA ’86, pages 224–231, 1986.

[13] A. Goldberg and D. Robson. Smalltalk 80: The Language and its Implementation. Addison-Wesley, Reading, Ma., 1983.

[14] IMDG-Code, International Maritime Dangerous Goods Code. International Maritime Organization, London, 1983.

[15] T. Koschmann and M. Walton Evens. Bridging the gap between object-oriented and logic programming. IEEE Software, pages 36–42, Jul. 1988.

[16] R. Kowalski and D. Kuehner. Resolution with selection function. Artificial Intelligence, 2(3):227–260, 1970.

[17] J.W. Lloyd. Foundations of Logic Programming. Springer Verlag, Berlin Heidelberg, 1987.

[18] F. Mellender. An integration of logic and object-oriented programming. ACM SIGPLAN Notices, 23(10):181–185, 1988.

[19] Bertrand Meyer. Object-Oriented Software Construction. Prentice Hall, New York, 1988.

[20] CHEMDATA. National Chemical Emergency Centre, B7.22 Harwell Laboratory, United Kingdom Atomic Energy Authority, Oxfordshire OX 11 ORA.

[21] K. Nygaard. Basic concepts in object oriented programming. ACM SIGPLAN Notices, 21(10):128–132, Oct. 1986.

[22] Z. Palaskas, P. Loucopoulos, and F. van Asshe. AMORE — object oriented extensions to Prolog. In TOOLS ’89, 1989.

[23] G.A. Pascoe. Elements of object-oriented programming. McGraw-Hill BYTE-Magazine, pages 15–20, Aug. 1986.

[24] H. Quester, editor. Bundesgesetzblatt Teil I Nr. 30 vom 4. Juli 1986. Verkehrsverlag, Düsseldorf, 1986. Anlageband 2.

[25] T. Rentsch. Object oriented programming. ACM SIGPLAN Notices, 17(9):51–57, Sept. 1982.

[26] Antoine Rizk, Jean-Marc Fellous, and Michel Tueni. An object-oriented model in the concurrent logic programming language Parlog. Technical Report 1067, Institut National de Recherche en Informatique et en Automatique (INRIA), Domaine de Voluceau Rocquencourt, B.P.105, 7853 LeChesnay Cedex, France, August 1989.

[27] D. Robson. Object-oriented software systems. McGraw-Hill BYTE Magazine, 6(8):74–86, Aug. 1981.

[28] A. Schmidt and F. Belli. An extension of Prolog for object-oriented programming in logic. In Proceedings of the 3rd International Conference IEA/AIE, pages 1153–1163, 1990.

[29] A. Snyder. Encapsulation and inheritance in object-oriented programming languages. In Proc. OOPSLA ’86, pages 38–45, Sept. 1986.

[30] M. Stefik and D.G. Bobrow. Object-oriented programming: Themes and variations. AI Magazine, 6(4):40–62, 1986.

[31] Stowage & Segregation Guide to IMDG-Code. K.O. Storck Verlag, Hamburg. See also [14].

[32] J. Thom and J. Zobel. NU-Prolog reference manual, version 1.3. Technical Report 86/10, Department of Computer Science, University of Melbourne, 1988.

[33] D. Thomas. What’s in an object. McGraw-Hill BYTE Magazine, 14(3):231–240, Mar. 1989.

[34] INFUCHS, Informationssystem für Umweltchemikalien, Chemieanlagen und Störfälle. Umweltbundesamt, Gruppe Z2, Information und Dokumentation, UMPLIS, Berlin.

[35] Carlo Zaniolo. Object-oriented programming in Prolog. In IEEE Symposium on Logic Programming, 1984.