[Next] [Previous] [Up] [Top] [Contents]

CHAPTER 3

3.5 An Ontology for Integrating Domain and Code-Level Knowledge

There are two principle reasons, as has been discussed previously, why it is desirable to merge the previously separate domain and code models: an integrated model would prevent the domain model from inaccurately representing the implicit domain knowledge in the code model, and having domain knowledge "tied" directly to the code model object facilitates understanding those object (this will be fully justified in Chapter 6).

At the outset it may not seem that difficult to merge the two models into one, yet in previous SISs where it was deemed desirable it was not accomplished [Selfridge, 1990] [Devanbu, 1994]. This provides at least an initial clue that the apparent simplicity of the problem is deceiving. Considerable time and effort over the course of this research was expended verifying the difficulty of this task and locating the source of that difficulty. The problem turns out to lie at the very foundation of Knowledge Representation and Reasoning as a computational enterprise, a place few thought to look, and its identification is leading to a new epistemology for representing software [Welty, 1995a].

3.5.1 First Order Representation

Most symbolic representation systems, including all KR systems, are based on First Order Logic (FOL), and thus have two basic kinds of symbols: predicate symbols and object symbols [Carnap, 1961]. Object symbols denote instances or individuals, and predicate symbols denote properties or attributes of those individuals.

Although actual usage varies, the logical foundation is clear: set membership is a unary predicate, and therefore the name of a set is a predicate symbol [Carnap, 1947] [Quine, 1964]. This point may seem obvious or irrelevant, but the simple fact is that many practitioners ignore it, and it does come into play in the realm of software representations.

3.5.1.1 Concepts, Individuals, and Roles

A common representation in FOL is something like the following:

Eagle(E1)
Number(10)
Age(E1,10)
Those accustomed to reading representations will interpret this as, "E1 is an Eagle, 10 is a Number, and E1's age is 10." This is not actually what it says according to the semantics of FOL, but because most representations follow this general scheme, implemented representation systems (such as Classic) present scaled down first order systems which allow for the definition of three special kinds of symbols: concepts, individuals, and roles.

An individual is similar to an object symbol in FOL except that it must be an individual of some concept, as with E1 and 10. A concept is a special unary predicate that denotes a set, as with Eagle and Number. A role is a binary predicate that represents a relationship between two individuals, as with age. The interpretation given above would be correct for Classic.

3.5.1.2 Superclass Inheritance

Another common representation in FOL is:

FORALL x Eagle(x) --> Bird(x)
This would be interpreted as "All eagles are birds," which again is not entirely correct according to the semantics of FOL, but is used so frequently to mean precisely this that representation languages almost universally supply a short-hand notation for expressing this taxonomic relationship, called subclass.

Although the subclass relationship is usually expressed as a relationship between two concepts, e.g Eagle is a subclass of Bird, it is worthwhile to note that, by definition, relationships between predicate symbols (concepts are, as discussed above, predicate symbols) are second order, and it is important computationally to maintain the first order status of a representation system [Gödel, 1931]. The subclass relationship really is no more than a shorthand notation for the inference shown above.

Clearly, via modus ponens, the result of making Eagle a subclass of Bird would be that the individual E1 in the previous example would now be inferred to be an individual of Bird. This is known as superclass inheritance.

3.5.1.3 Identifying Non-First Order Objects

The previous section mentioned the importance of maintaining a first order representation in a system. There are two common pitfalls of representing a domain that can cause a second order construct: individuals of individuals and roles between concepts.

Most representation languages do not permit an individual to have individuals. Consider the implications of such a construct in FOL:

Eagle(E1)
E1(E2)
When E1 is used as a predicate symbol, the predicate Eagle becomes second-order because it is the predicate of a predicate. A common pitfall of modeling in FOL is to create a two-place predicate for individual, such as:

individual(E1, Eagle)
individual(E2, E1)
Which, syntactically, is first order. It is not, however, completely first order because, as stated in the beginning of this section, set membership is a unary predicate. This example violates the semantics of a first order system [Carnap, 1947] [Quine, 1964].

The point here is that, despite syntactic hacks like making a predicate called individual, an individual of an individual is a second-order construct.

First order representation languages also do not allow roles between concepts. A role is a two place predicate, and a concept is a one place predicate, making the PreysOn role second order:

Pigeon(P1)
Eagle(E1)
PreysOn(Eagle, Pigeon)
PreysOn(Eagle, P1)
A concept can not, therefore, be predicated by a role (a concept can not be predicated by anything). It is common to speak of the relationship between an individual and its concept as a role, but this must be understood to be different than a role between two individuals.

3.5.1.4 Extensions to First Order Systems

Many representation systems provide small extensions that, while second order, are tightly controlled to avoid the computational problems associated with second order systems. Two examples of this are Smalltalk Meta-Classes and Classic Meta-Individuals.

Smalltalk [Goldberg and Robson, 1983] provides the ability for classes (a Smalltalk class is roughly equivalent to a Classic concept, at least for the purposes of this discussion) to have certain properties of instances (individuals). They can, like instances, be sent messages and have their own variables, which are defined as part of the meta-class description of the class. While this is second order, it is not prone to problems because this aspect of the representation is not subject to inference (Smalltalk provides only for superclass inheritance as a form of inference).

Smalltalk classes are also themselves instances of a special class named class, making them instances which can have instances. Again, this second order relationship is controlled because there is no actual inference involved, it is provided more to keep the syntax cleaner, and make the message passing paradigm pervasive in the language.

Classic employs an approach to representing roles on concepts that has been called the abstraction relationship [Brachman, 1983]. This approach involves creating, as part of the language, a special kind of individual for each concept which represents the concept as an individual. While these individuals (called meta-individuals) behave like all other individuals (having roles and being individuals of a concept called concept), they have a special relationship, the abstraction relationship (which is defined as part of the syntax of the language as the individual of relationship is), between themselves and the concept they represent. Each concept in Classic, then, can have individuals and a meta-individual.

Again, as with Smalltalk, this second order relationship can exist because it is not used in any inference.

3.5.2 Domain Objects and Code-Level Objects

Now we can turn our attention back to the problem of integrating the code and domain models, and pay particular attention to the observation that the domain objects turn up as data-types in the code-model. In the code model example shown in Figure 3.1, we see again that the individual data-type-08 represents the data-type group. There are, scattered throughout the code, a number of variables that have this data-type, and one example of such a variable, parameter-02, is shown in Figure 3.1. Looking back at Figure 2.4, we again see group in the domain model taxonomy under mail-recipient. It would seem that the ideal way to proceed would be to somehow indicate in the "merged" model that the domain concept group is the same as the code-level individual data-type-08. In Classic, the only ways to express a relationship between two objects are to link them with a role, make one an individual of the other, or make one the meta-individual of the other.

We cannot relate data-type-08 to group with a role, because the former is an individual and the latter is a concept. Any kind of link between them other than the individual link would create a second-order relationship. We do not want to make data-type-08 an individual of group because it simply isn't. Individuals of group should represent actual groups, not a data-type that has variables and is used by functions.

In essence, data-type-08 (an individual from the code model) and group (a concept from the domain model)[1] are the same object, viewed differently. One describes how groups are used by the program, the other describes groups in general. We have run into a situation in which we need to represent one object, the same object, as both a concept and an individual. Neither model would be satisfied if we simply chose one or the other. This is what the meta-individual relationship is supposed to represent, but the implementation of this relationship in Classic is a barrier: the point of representing data-type-08 as an individual is to use it in inference to make discovery easier (as will be shown in Chapter 6), but meta-individuals can not be used in inference.

This is the core of the difficulty in integrating the code model and domain model: they represent the same knowledge at fundamentally different levels: concepts and individuals. Even though the code and domain models reuse many of the same objects, they reason about them and use them differently, and to support both at the same time requires a second order representation.

3.5.3 Spanning Objects

No knowledge representation language is capable of supporting second-order representations except for small extensions that allow no inference such as in Smalltalk and Classic. Opening up a language to second-order constructs is known to be a problem, but a solution exists which at least allows for very tight control over such relationships [Welty and Ferrucci, 1994], and is being added to Classic.

The solution involves splitting second-order objects into two parts, and keeping each part in a separate universe of discourse in which it exists as a first-order object (again in Classic there are three kinds of objects: concepts, roles, and individuals). What is actually one object is being represented as two objects, and so we will refer to the actual object as a spanning object because its representation spans two universes of discourse. Each spanning object will be composed of an individual in one universe of discourse, and a concept or role in a second universe of discourse. The two parts will be linked by a special spanning function, which will be defined for each class of second order objects. This function defines how to generate the concept or role part from the individual part.

Consider, for example, the data-type problem. Again, we need to be able to use data-type-08 as an individual in the code model so that it can be linked into the program, and we need to be able to use group as a concept in the domain model so that it can have individuals and describe what groups are in the domain. We also need to be sure that the domain model concept accurately reflects what the code "knows" about groups.

To accommodate these requirements with spanning objects we create two universes of discourse, one for the code model and one for the domain model, as shown in Figure 3.2. In the code model universe, the concept data-type is created and identified as a class of spanning objects (this means that the concept data-type is not itself a spanning object, but its individuals are), and we define a spanning function which specifies the relationship between the two parts of the spanning objects. We now create an individual of data-type, data-type-08, which is a spanning object. The second part of the object, the concept group, is then created in the second universe of discourse. The concept description for group is generated from data-type-08 automatically by the spanning function, and now individuals of group can be created in the domain model universe.

Let us consider more deeply the objects in the code model. In general we know that an object-oriented data-type is defined by its slots, methods, and its superclasses (a relationship to another data-type through which it can inherit other slots and methods). Each of these things needs to be represented explicitly in the code model universe - that is, we need concepts describing slots, methods, and a role representing the superclass relationship (we need a role here because the relationship is between two individuals of data-type, and not between two concepts). In the domain discussed so far (the taxonomy is shown in Figure 2.4), a group is a subclass of a mail recipient, and has one slot that is filled with the members of that group, all of which must be people. This could be represented, as shown in Figure 3.3, by creating an individual of the concept slot, and making this a filler for the role has-slot in the individual data-type-08. This individual of slot will have a role called has-data-type, which is filled with the individual data-type-05, the individual that represents people. In the figure, the individuals are shown with their classic names (such as data-type-05), and the value of the string that fills their name role (such as "person").

There are two kinds of spanning objects shown in Figure 3.3: data-types and slots. A data-type maps from an individual in the code-model universe to a concept in the domain model universe. A slot maps from an individual in the code model to a role in the domain model universe. In fact, much of what is shown in Figure 3.3 maps into the domain model universe. The superclass role maps into a told subsumption relationship (in other words the role superclass maps into the built-in Classic superclass relationship between two concepts - the role superclass has no special meaning to Classic), the has-slot role maps into a role specification in the domain model universe (in other words, every individual of slot in the code model universe is a role in the domain model universe), and the has-data-type role maps into an "all" restriction, as shown in Figure 3.4.

Most of the work in creating the spanning concepts in the domain model universe is done by the spanning function for individuals of data-type. This function generates a concept description from each individual. It finds the filler for the name role (shown in Figure 3.3 under each individual) and uses this as the name of the concept. It then builds the concept description by creating a list that starts with the word and, then it finds the fillers for the superclass role and for each one gets the filler for the name role and inserts it after the and. If there are no fillers for the superclass role, the symbol classic-thing is used. Next, for each filler for the role has-slot, the filler for the has-data-type role is retrieved, and this name is used in an all restriction on the role whose name is retrieved from the name role of the slot individual.

The spanning function for individuals of slot simply creates a role definition, using the value in the name role as the name of the role.

The resulting Classic code for the domain model based on the code model objects from Figure 3.3 is shown in Figure 3.4, below:

(defrole has-members)

(defconcept mail-recipient
  (and classic-thing))

(defconcept group
  (and mail-recipient
       (all has-members person)))

(defconcept person
  (and mail-recipient))
Note that all the Classic code shown in Figure 3.4 is generated automatically from the code model by applying the spanning functions to the spanning objects (individuals of data-type and slot). In other words, the domain model is generated automatically from the code model. This implies that all the information in the domain model must be part of the code model. In fact, every domain concept must be represented as a data-type. Normally the code model represents only those aspects of data-types that are relevant to the software operation, but the consequence of having the domain model depend on the code model is that the code model must include some additional information. For example, it may not be relevant to the software that all people have a role called spouse, the software may never use this information (in other words, the slot may never get accessed in the program), yet this is important information to the domain model, so the slot must exist in the code model so that the proper domain concept can be generated.

We have identified a new element of a Classic ontology: the spanning object. This requires that any Classic ontology that uses spanning objects add the specification of the universes of discourse, the spanning objects, and the spanning functions to the definition of what goes into a Classic ontology given in Section 2.2.4.

3.5.4 The Code-Level Ontology

It is important to note that while the code model includes the domain model, the code-level ontology does not include the domain ontology. The code-level ontology is basically a description of the language through which the code model can be described. It will be in describing the code model individuals (more specifically, the individuals of data-type and slot) that the domain ontology will be described. This section gives the specification of the code-level ontology.

Again, as discussed in Section 2.2.3 and Section 2.2.4, and with the addition of spanning objects from Section 3.5.3, an ontology is composed of concepts, roles, and rules. The concepts are arranged in a taxonomy, identified as defined or primitive, given role restrictions, and if a concept is a spanning object, it is given a spanning function. Roles are arranged in taxonomies and given inverses.

3.5.4.1 Code-Level Concepts

The taxonomy of code-level concepts is shown in Figure 3.5. The complete Classic listing of the code-level concepts is given in Appendix A. In this section, each concept will be described briefly, including some of its more pertinent roles. This ontology is for an object-oriented language. All the concepts shown in this section are primitive Classic concepts.

3.5.4.2 Code-Level Roles

The complete Classic listing of the code-level roles is shown in Appendix B. There are seven major role taxonomies used at the code level. Each of these is shown in Figure 3.6. Most of the relevant roles are described in the previous section, a brief account of the role types and other aspects of the role ontology will be given here.

The seven types of roles are part-of, control-flow, accessed-by, specialization-of, data-slot, meta-role, and instance-of. The part-of roles relate objects and actions to their parts, for example the parts of a data-type are its slots and method, so slot-of and method-of are roles in the part-of role taxonomy. It should be noted that has-parts is the inverse role to part-of, most cases the inverse of a role is fairly obvious from the name. The inverse of slot-of is has-slots, and the inverse of method-of is has-methods, etc.

The control-flow roles specify the flow of control, as discussed in the previous section.

The specialization-of roles relate a more specific individual to a more general one. For example, the superclass role relates an individual of data-type to another individual of data-type that is more general (it relates a data-type to its superclass). There are three special kinds of specialization links other than superclass. The specialized-method-of role links a method to a more general method. This role is used to prevent inheritance along the superclass hierarchy of a method from a more general class to a more specific one if a more specific version of that method exists at the more specific data-type. This is explained in more detail in the next section, and is also true of the specialized-slot-of role. The third role, interface-method-of, relates a method to one that it is an interface for. This relationship is described in more detail in Chapter 6.

The instance-of roles specify the relationship between a descriptive object, such as a method or data-type, and an instance of that object, such as a variable or message (method invocation). The use of this class of roles is required because this ontology generates another universe of discourse in which methods and data-types are concepts. It should be noted that this is different from the built-in Classic relationship that has been referred to as individual-of.

The data-slot roles are the simplest roles that link individuals of code-level concepts to host individuals. A host individual, as described in Section 2.2.4, refers to built in data such as strings, numbers, etc.

The meta-role roles are used by the user interface, and are described further in later sections.

The accessed-by roles are one of the most useful discovery tools provided by this representation. These roles are all the inverses of the roles used by the code-level actions to specify the objects the actions will work on or with. For example, individuals of assignment have two roles: changes, which identifies the variable or slot to be changed, and new-value which identifies the new value for the variable or slot. The inverse of changes is changed-by, and as a result, every variable and slot automatically has a changed-by link to every assignment statement where it is changed. These roles are discussed in more detail in Chapter 6.

Finally, the roles that are involved in the data-type hierarchy, superclass, has-methods, and has-slots, all have two role subtypes: immediate and inherited. The need for these role types is discussed in the next section.

3.5.4.3 Code-Level Rules

The complete Classic listing of all the code-level rules is given in Appendix C. Most rules are used to aid discovery, and as a result are described in more detail in Chapter 6. The rule facility in Classic has a number of dependencies which are important to understand during the development of a software system, and these are described in more detail in Chapter 4. In this section, the four major classes of rules are described.

Most of the rules in the code-level ontology fall into one of four categories: rules for determining decomposition, rules for determining the parts of a code-block, rules for the data-type hierarchy, and rules for describing actions. Aside from the latter type, all rules are specified as paths (see Section 2.2.4.2). The rules for decomposition and describing actions are used predominantly to support discovery, and the rules for determining parts of a code-block are used predominantly to support development. The data-type hierarchy rules present numerous very special circumstances, and will be discussed in this section.

This ontology describes an object-oriented language. An important ingredient in any object-oriented language, as discussed in Section 2.1.5, is inheritance. Inheritance is a very general inference facility that subsumes all forms of natural deduction [Fox, 1979], and the type employed by object-oriented languages is actually a very simple form, called superclass inheritance. Superclass inheritance involves the inheritance of three distinct kinds of information: superclasses, slots, and methods.

The first part of superclass inheritance, inheriting superclasses, is illustrated in Figure 3.7. The solid lines show the told superclass relationships, and the dotted lines show the inherited or inferred links. In this example, since list-of-integers is a subclass of list-of-numbers and list-of-numbers is a subclass of list, we infer that list-of-integers is also a subclass of list.

The second part of superclass inheritance, inheriting slots, is illustrated in Figure 3.8. In this figure, the solid, unlabeled lines represent the superclass relationship, the labelled lines represent slot definitions, and again dashed lines represent inherited information. In this example, since list-of-people is a subclass of list, it inherits the number-of-elements slot from list. This slot has a restriction that all values must be of the type number. The elements role is not inherited, however, because list-of-people already has a more specialized version of that slot. This is an example of specialization override inheritance, where a value is not inherited if a more special one already fills the role.

The third part of superclass inheritance, inheriting methods, works in precisely the same manner as the inheritance of slots. All methods of a superclass are inherited to the subclass, except those that already exist in the subclass.

Classic, like any knowledge representation language, maintains a facility for supporting superclass inheritance between concepts, which creates a problem for this representation because the classes are individuals of the concept data-type. There is no built-in facility in Classic for superclass inheritance between individuals, so another mechanism must be used to implement this critical feature. With some representation additions, the path facility turns out to be powerful enough to specify this type of inheritance with three rules.

The representation addition is in the form of a sub-type of the three roles involved in the data-type hierarchy (has-methods, has-slots, and superclass). Each of these roles has two subtypes, immediate and inherited (e.g. has-immediate-methods, has-inherited-methods, immediate-superclass, inherited-superclass, etc.). The immediate version of each role links an individual of data-type to the "local" information for that role (this has been illustrated in Figure 3.7 and Figure 3.8 as solid lines), and the inherited version of each role links the data-type to any inherited information (shown as dotted lines in the figures). The "normal" roles combine the immediate and inherited information because they are the parent roles.

The need for the immediate and inherited versions of each role becomes clear when the path facility is considered. Without these special subroles, the rule for e.g. inheriting superclasses would be:

The infinite loop in this rule should be clear, it is analogous to the Prolog case of specifying a predicate as the first part of its own derivation. There are similar loops for methods and slots. The immediate and inherited versions of the roles allow a grounding of the different kinds of superclass inheritance. The correct rules are:

When these rules are combined with the role taxonomy, the superclass, has-methods, and has-slots roles are filled with all the immediate and inherited fillers. These rules imply an ordering of the inference, as well. The rules won't work for a data-type "lower" in the hierarchy unless they have already fired on the data-types above it. This is taken care of by Classic if the taxonomy is specified, and the roles closed, in the correct order. This requirement is discussed further in Section 4.1.1 and Section 7.1.3.3.

There is still an element missing from the rules for inheriting slots and methods: specialization override. This presents another problem for the Classic representation. The mechanics of specialization override in object-oriented languages are based on the name of the slot or method: if the name is the same, inheritance is blocked. This mechanism is overly simplistic, however. Having the same name does not necessarily imply that the slot or method is more specialized. In this ontology, the relationship must be explicit - for a rule to block inheritance of an object there must be an individual already filling the role that is linked by specialization-of (or any subrole) to the object that should be blocked. This process is illustrated in Figure 3.9, where again dotted lines are used to indicate derived information. The dotted line linking data-type-07 to slot-02 is the only inference resulting from superclass inheritance. The inheritance of slot-01 to data-type-07 is blocked by the fact that slot-03 is a specialized-slot-of slot-01, and specialized-slot-of is a subrole of specialization-of (see Figure 3.6 on page 66). Note also that since has-slots is the parent role of both has-inherited-slots and has-immediate-slots, the (derived) fillers for the has-slots role on data-type-07 are slot-02 and slot-03.


Chris Welty - Dissertation - 17 SEP 1996
[Next] [Previous] [Up] [Top] [Contents]

Generated with Harlequin WebMaker