In presenting the functional interface of the Generic Frame Protocol (GFP), we first describe concepts that are common to many operations, including common arguments and standard ways to signal errors. Then we give an overview of all the GFP operations, based on the type of objects on which they operate. To facilitate the use of GFP with multiple programming languages we describe language bindings for C, Lisp, and Java. Finally, we list all the GFP operations, along with their input arguments, return values, and descriptions.
The concept of KB behaviors, common to many GFP operations, is described in detail in Chapter 4. On the first reading of this chapter, any references to KB behaviors can be safely ignored.
GFP assumes a client-server architecture for application development. The client and server can be on different machines, on the same machine but in a different address space, or on the same machine and in the same address space. When the GFP client and server are on different machines, they communicate via the GFP transport layer. The GFP transport layer can be implemented by direct procedure calls if the client and server are in the same address space, or any of the standard network protocols (e.g., CORBA or TCP/IP).
Using GFP, a client application can access any FRS that supports a GFP server. GFP enables an application to access multiple GFP servers, each of which may support multiple FRSs, which in turn may provide access to multiple KBs. For example, an application that merged multiple KBs could access a LOOM KB on one server, two Ontolingua KBs on a second server, and place the result in a Classic KB on a third server.
To access a KB, an application loads the GFP client library for the appropriate programming language. For instance, an application written in the Java programming language would be compiled with the GFP Java client library. The client library defines Java methods for all of the GFP operations. It also defines Java classes to implement all of the GFP entities, and conditions to implement all of the GFP conditions.
To access a GFP server, a client application typically undertakes the
following steps. First, the application must establish a connection
to a GFP server (using establish-connection
). Second, the application
may find out the set of FRSs that the server supports on that
connection (using get-kb-types). Third, the application can get
information about the KBs of a given type that can be opened on the
connection (using openable-kbs). Finally, the application can
either open a specific KB (using open-kb) or create a new one
(using create-kb). The application may now query and manipulate
the KB by executing GFP operations. For example, it can look for
specific frames (using get-frames-matching), find the root
classes of the KB (using
get-kb-roots, create new frames (using create-frame), and save
changes (using save-kb).
A back end implementor implements the GFP operations by using the FRS's native API. Whenever there is no direct mapping between a GFP operation and some operation in the native API, the back end may invoke multiple operations in the native API or perform some runtime translation between the constructs of GFP and the constructs supported by underlying FRS. For example, LOOM supports a concept definition language but no facets. It is possible to translate the LOOM\ concept definition language into GFP facets, for example, the :at-most concept constructor of LOOM is translated into the :maximum-cardinality facet.
There is considerable redundancy in GFP operations -- some operations can be implemented in terms of others. For example, consider the operations class-p and individual-p that check whether an entity is a class or on individual. If we define either of these two operations, it can be used to define the other, because the domain of discourse is partitioned into classes and individuals. The minimal set of GFP operations that must be implemented, also known as mandatory methods, is known as a kernel. Because of this redundancy, an implementor can choose a kernel that most closely matches the native API of an FRS.
It is possible to develop a system that implements the complete protocol using a set of kernel operations. At least two such system have been implemented. As a consequence, an FRS developer can implement a complete GFP-compliant system by implementing only the kernel operations. This may lead to some loss in efficiency because some non-kernel operations may have more efficient implementation in terms of the native API of an FRS. For example, the operation get-kb-classes can be implemented using get-kb-frames and class-p operations. Systems that store an explicit list of classes can return this result more efficiently.
FRS developers can also support subsets of GFP behavior in their back ends, and then advertise this using the behaviors mechanism (see Section 4.1). For instance, a back end might only define read-only operations on a KB. This further reduces the initial effort required to implement GFP.
Many GFP operations, (for example, slot-p) are predicates on their arguments, and the value returned by them is a boolean truth value. All of these operations are named consistently with ``-p'' as a suffix. For example, slot-p is a predicate that is true for slots, and class-p is a predicate that is true for classes. When we say that an operation returns a boolean result, we mean that it returns either the GFP value false, which is to be interpreted as the boolean value false, or some non-false value is returned that is interpreted as the boolean true. Although such a truth-deciding value will frequently be the GFP value true, the only guarantee is that it will not be false. In some language implementations, the GFP values true and false may not be the same as the native language values for true and false.
We use a standard format for describing the GFP operations in Section 3.7. To explain this format, we use an example description of the get-slot-values operation as shown in Figure 3.1.
Figure 3.1: Format for documentation of GFP operations
Name. The first element is the name of the operation.
Argument list. The arguments are specified using the Common Lisp conventions. The argument list is divided into two groups, separated by the ``&key'' indicator. Mandatory arguments precede ``&key''. These arguments must be provided in all language bindings. The keyword (named) arguments follow the ``&key'' indicator. In Section 3.6, we discuss language bindings that show how the keyword arguments should be interpreted for languages such as C that do not support keyword arguments. In some language bindings, the keyword arguments may be optional. In language bindings that support optional keyword arguments (for example, Java), default values will be supplied for the optional keyword arguments. The specification of an optional keyword argument is either (1) a list whose first element is the argument name and whose second element is the default value or (2) the argument name. Otherwise, the argument defaults to false .
For example, kb-local-only-p defaults to false , and inference-level defaults to :taxonomic.
Return values. Following ``
'' are the values
returned by the operation or void if no value is returned. The
operations may return more than one value. For example,
get-slot-values returns three values -- a list containing
the requested slot values, exact-p and more-status.
Flags. Each operation has a set of descriptive flags to indicate
(1) whether there is a corresponding enumerator operation
(E), (2) whether it is mandatory, optional or
neither
in the current implementations (M
or O or I), and (3) if it is read-only or writes to the
KB (R or W). For example,
get-slot-values has an enumerator, is read-only, and is optional
in the current implementation.
Documentation. The documentation defines the meaning of the operations. For brevity, common arguments, such as inference-level, and common return values, such as more-status, are not described in the individual operation definitions, but in Sections 3.3 and 3.5. Arguments, return values, and GFP operations are in this font.
A comprehensive list of all arguments that are common to many GFP operations, and their default and legal values, is shown in Table 3.1. Some of the common arguments are described later, when we present an overview of the GFP operations (Sections 3.5).
In many situations, it is necessary to control the inferences that should be performed while executing GFP operations. For example, while retrieving the values of a slot, a user may wish to receive directly asserted values and perhaps inherited values as well. To provide such control, many GFP operations support an inference-level argument that can take the following three values.
A value of :direct for inference-level is guaranteed to return at least directly asserted values, because in some systems, such as, forward chaining systems -- values in addition to the directly asserted values may also be returned. Some GFP operations, for example, an operation returning superclasses of a class may eliminate redundant values from the result. To help an application determine when exactly the directly asserted values are returned, all operations taking an :inference-level argument will return a second value (the first value being the result of the operation), exact-p, which is true if it is known that exactly the :direct or :taxonomic values is returned. A GFP implementation that always returns false as the value of exact-p is compliant, but implementors are encouraged to return true whenever possible.
For a GFP operation that returns a list of values, it is often desirable to limit the size of the resulting list and the computation necessary to establish the elements of the list. The number-of-values argument, which is supported by many GFP operations, allows the caller to limit the number of values returned. The legal values for this argument are :all, which is always the default, or any positive integer. A GFP operation accepting the number-of-values argument returns at least three values. The first value is the result list for the request, the second is the value of exact-p (discussed in the Section 3.3.1), and the third is more-status, a value that reveals whether there are more results available.
When number-of-values is :all; the value of more-status is either false, which indicates that there are known to be no more results, or :more, which indicates that there may still theoretically be more results, but the FRS was unable to find any more. When number-of-values is :all, a more-status value of :more means that the system cannot guarantee that it can access all possible results requested by the GFP operation.
If the number-of-values argument is a positive integer, then no more results than the specified number will be returned. The more-status will be one of the following three possible values.
| Description | Argument Name | Default Value | Constraint on a Legal Value X |
| Connection | connection | gfp-local-connection | ( connection-p X) |
| KB Type | kb-type | KB-type object | |
| KB reference | kb | (current-kb) | ( kb-p X) |
| KB inclusion | kb-local-only-p | false | X |
| Frame reference | frame | ( coercible-to-frame-p X) | |
| Slot reference | slot | ( slot-p X) | |
| Facet reference | facet | ( facet-p X) | |
| Class reference | class | ( class-p X) | |
| Value | value/thing | Any value corresponding to GFP data types (portable) or any value corresponding to system-defined data types | |
| Number of values to be returned | number-of-values | X=:all or a positive integer | |
| Inferences to be performed | inference-level | :taxonomic | X |
| Test function | test | (kb-test-fn) | X |
| Choosing values | value-selector | :known-true | X |
| Controls whether error should be signaled | error-p | true | X |
| Enumerator | enumerator | An enumerator object |
As discussed in the knowledge model, GFP provides a facility to store and retrieve default values. Therefore, while adding a value to a slot or a facet, one may specify whether the value being added is a default value. Similarly, while retrieving values from a slot (or facet) of a frame, one may choose whether default values should be returned. This control is provided by the value-selector argument. The argument value-selector may take one of the following three values.
Many GFP operations need to perform comparisons. For example, while removing a slot value, we need to compare the value to be removed with the existing slot values. GFP operations requiring such comparisons support a test argument whose value is one of :eql, :equal, :equalp, or a procedure that should be used in the comparisons. The test argument defaults to :equal. The functions invoked for the values :eql, :equal, and :equalp are eql-in-kb, equal-in-kb, and equalp-in-kb respectively. The arguments to a test procedure are the two entities to be compared, and the KB for which the comparison should be performed, and kb-local-only-p (discussed in Section 3.5.2). The test function should return either true or false. Supplying a function in the native programming language as a value of test is not portable especially in network applications.
To support the development of robust applications, some standard means for processing error conditions are desirable. Whenever possible, GFP operations that experience erroneous conditions or arguments signal errors. GFP uses condition signals that are analogous to COMMON LISP\ conditions or Java exceptions. Condition signals are defined for commonly occurring error situations. GFP provides a user with some control over when the errors should be signaled. A comprehensive list of standard GFP errors is available in Section 3.8.
The GFP errors are said to be either continuable or not. An error is said to be continuable only if the state of the KB is not known to have been changed by the error in such a way that the behavior of subsequent GFP operations becomes undefined. Thus, although the signaling of a continuable error will interrupt any processing currently being performed, subsequent GFP calls will be unaffected. After a non-continuable error, the state of the KB and the behavior of the FRS and application are undefined. The operation continuable-error-p returns true only for continuable error objects, and is false otherwise.
The error behavior of GFP operations is specified in one of the following four ways: ``is an error'', ``error is signaled'', ``unspecified behavior'', ``is not portable''.
Whenever we say that ``it is an error'' for some situation to occur, it means that no valid GFP program should cause that situation to occur; if it occurs, the effects and results are completely undefined. No GFP-compliant implementation is required to detect such errors, but implementors are encouraged to detect such errors when possible. In some cases we also specify the condition that should be signaled if an implementation decides to signal one and has choice over which error is signaled.
When we say that an ``error is signaled'' in a situation, any GFP-compliant implementation must signal an error of the specified type when that situation is encountered.
When we say that ``behavior is unspecified'' for a given situation, it means that a GFP-compliant system may or may not treat that situation as an error.
An example of a situation where the behavior is unspecified is the function replace-slot-value that replaces an existing slot value with another. The definition of replace-slot-value does not specify whether an error should be signaled when the slot value to be replaced is not a current value of the slot, or if the new value to be added is already a value of the slot. This approach simplifies the implementation and speeds performance since each underlying FRS may make different assumptions about signaling behavior.
When we say that a particular usage is not portable, we mean that an application may legally operate in this state, but even if correct behavior is observed, it is not guaranteed to be the case if the application is directed at a different knowledge base (KB) of the same FRS, or the same KB with different behavior settings, or a KB resident in a different FRS (even with the same content), or a KB connected over a network. For example, many KBs uniquely identify frames by their names. For a KB with a value true for the behavior :frame-names-required, a frame name may be used as a valid argument for any operation that accepts a frame argument. Since the :frame-names-required behavior can be false for some KBs, such usage is not portable, because a frame name will not always be a valid argument for an operation that accepts a frame argument.
The general rule is that a standard GFP error will be signaled when the documented exceptions occur. If, for example, the user attempts to retrieve slot values from a frame that does not exist, by using a call to the function get-slot-values, the error not-coercible-to-frame is signaled. Errors can also be signaled when slot constraints are violated. When a constraint violation occurs, the FRS should signal one of the conditions defined for constraint violations.
In two situations, an application program needs explicit control of whether an error should be signaled. An example of the first situation is the function coerce-to-frame that coerces a given object to a frame. In general, coerce-to-frame signals an error if the input object cannot be coerced to a frame. But a user program may not wish an error to be signaled if it is not sure if the input object is indeed coercible to a frame. The second situation is encountered in functions such as copy-frame that perform multiple operations. For such functions, a user may wish to continue copying even if there is an error, in which case the copy-frame continues copying as much as possible.
To provide such control, a few GFP operations accept an error-p argument. The default value for error-p is true, in which case the errors are signaled as usual. If error-p is false, the operation catches at least the documented conditions and continues execution if possible.
The GFP operations accepting the error-p argument return a second status value, in addition to the usual return value. When the second value is true, and error-p is false, it means that execution completed successfully.
We categorize all the GFP operations based on the type of objects in which they operate. We introduce here any new concepts that are necessary for understanding those operations, and list operations in each category. Detailed descriptions of all operations are in Section 3.7.
GFP has been designed to be easily used in both centralized and distributed environments. To permit this, we use an abstraction called a connection that encodes the actual location of a GFP KB and mediates communication between a GFP application and the KB. To communicate with an FRS, an application program first establishes a connection by using the establish-connection operation. With each GFP operation, we need to indicate the connection that should be used in executing it. Some GFP operations take an explicit connection argument, whereas others derive the connection from a KB argument (explained in Section 3.5.2). Thus, once a connection is established, a user need not be aware of the actual location of the KB or whether the KB is being accessed from the same address space as the application or over the network.
Depending on the nature of the communication and the security model involved, different types of connection are used to perform the mediation between a GFP client and a server. For example, if a GFP server requires user authentication, the client will have to fulfill the authentication requirement before being able to establish a connection. The GFP specification does not provide a detailed definition of connections since the specific behavior of the connections will be a function of the communication, threading, locking, and security models used by the server. A provider of a GFP server is required to make the definition of its supported connection types available to the authors of GFP client programs.
The connection operations are: all-connections, close-connection, connection-p, establish-connection, local-connection
Network GFP operations take a kb argument that specifies the KB to which the operation applies. Multiple KBs may be open for access simultaneously by a GFP application. Different KBs may be serviced by different FRSs. For example, a single application might simultaneously access both a LOOM and a THEO KB.
With each KB in an FRS, we associate a GFP KB-object and a KB-locator. A KB-object uniquely identifies a KB (discussed below) in a GFP application. A KB-locator is a frame in the meta-KB and contains sufficient information to locate and open the KB. For example, for a KB residing in a file, a KB-locator may contain the name of the KB and its pathname. In effect, a kb-locator is to a KB as a filename is to a file. GFP operations requiring a kb argument must be supplied with a KB-object. The kb argument defaults to the current KB, which is queried by the GFP operation current-kb and set by the operation goto-kb.
GFP defines a taxonomy of KB-object types. An FRS may have several KB-object types defined for it. For example, one can imagine a type hierarchy in which the root class is ``KB'', with a subclass ``LOOM-KB'', which in turn has subclasses ``LOOM-FILE-KB'' and ``LOOM-ORACLE-KB.'' A back end implementor may define new KB types for her purpose, for example, ``CYC-KB''. Methods defined for KB types can be used to implement the type-specific KB operations. Certain GFP operations must be performed before an application has created a KB or acquired a reference to a KB object. Such operations must be supplied a kb-type argument, which should be the type object for the KB class for which the operation is to be executed. A KB type object for a KB class can be obtained by using the GFP operation get-kb-type.
Our model of KB access allows KBs to reside on different types of secondary storage devices, including both flat files, and database systems on remote servers. We do not assume that the KB is read into memory in its entirety when the KB is opened for access. An existing GFP KB can be opened using the GFP operation open-kb. A new GFP KB can be created using the GFP operation create-kb. An open KB may be saved using save-kb or closed using close-kb.
The GFP operation openable-kbs can determine which KBs are available on a given connection and return KB-locators for such KBs. Each such KB-locator can then be used as an argument to open-kb.
Many FRSs allow the creation of new KBs by including existing KBs. The semantics of KB inclusion vary widely amongst FRSs. It is often useful to limit the scope of an operation so that it is performed to exclude any frames belonging to KBs that were included in the current KB from some other KBs. For example, if a client application wants to display a class graph, it might want to select the roots of the graph by ignoring frames from any included KBs. To control such behavior, many GFP operations take the argument kb-local-only-p, which when set to true ignores, if possible, any information in the KB that was included from another KB. The kb-local-only-p argument always defaults to false.
As was mentioned in Chapter 2, the entities in the domain of discourse of interest to GFP exist within a knowledge base (KB). Because KBs themselves are of interest to us, every GFP implementation must define a special KB, called meta-KB containing frames that represent KBs themselves. Such frames representing the KB are the same as KB-locators. A meta-kb can be accessed using the GFP operation meta-kb.
The meta-KB also contains classes corresponding to each kb-type. Each KB-locator is an instance of an appropriate kb-type in the meta-KB. For example, a KB-locator representing a LOOM KB may be an instance of loom-kb KB-type.
The meta-KB allows the use of the GFP operations to manipulate KB-locators. For example, we could view the contents of a KB-locator using the GFP operation ( print-frame kb-locator :kb ( meta-kb)). Similarly, we could use get-class-instances to determine all the instances of the KB-type loom-kb as follows.
(get-class-instances (get-kb-type 'loom-kb) :kb (meta-kb)
:inference-level :taxonomic)
The KB operations are close-kb, copy-kb, create-kb, create-kb-locator, current-kb, expunge-kb, find-kb, find-kb-locator, find-kb-of-type, get-kb-direct-children, get-kb-direct-parents, get-kb-type, get-kb-types, get-kbs, get-kbs-of-type, goto-kb, kb-p, meta-kb, open-kb, openable-kbs, revert-kb, save-kb, save-kb-as
In GFP, as many as four identifiers are associated with a frame: frame name, frame handle, frame object, and pretty-name. Many FRSs associate a name with each frame in a KB. The frame name can be of type symbol or string. When the :frame-names-required behavior is true, a frame name uniquely identifies a frame. Some FRSs do not use frame names. GFP parameterizes this behavior of an FRS by setting the :frame-names-required behavior. When the value of :frame-names-required is true, it means that each frame is required to have a name, the frame name is unique in a KB, and the frame name supplied at the time of frame creation can be used at a later time to identify the frame. When the value of the :frame-names-required behavior is false, frame names are not required, and may not be unique in a KB even if supplied; one may not be able to locate a frame by using the name that was supplied when the frame was created.
A frame handle always uniquely identifies a frame in a KB. For some FRSs, a frame handle may be the same as the frame name or the same as the frame object. Frame handles are always supported by all the GFP back ends. To ensure portability, GFP application writers are encouraged always to refer to frames by frame handles.
A frame object is a reference to the actual data structure that encodes the information about the frame in an FRS. A frame object always uniquely identifies a frame in a KB. Some FRSs may not have a single data structure representing a frame. In such cases, the frame object is not distinguishable from the frame handle, and all frame-like properties of the frame are emergent from the context of the KB. For example, if we view an object-oriented database as an FRS, then object-identifiers (OIDs) could be viewed as frame handles. The information about an object identified by an OID does not all reside in one physical data structure in the database, but the contents of that object can still be viewed using GFP.
Any GFP operation that takes a frame argument must accept either a frame handle or a frame object as a valid value. A frame name is guaranteed to be a valid value for the frame argument only if the :frame-names-required behavior is set to true.
The frame pretty-name is a name for a frame that is meant for use in a user interface that needs to display a visually appealing but terse presentation for a frame. A pretty-name need not be unique or suitable for use in a program except for display purposes.
In Table 3.2, we summarize how different frame properties differ along several dimensions. These properties might differ in a complex implementation as follows. The frame name is a symbol unique in a KB, but not across different KBs (different KBs can contain different frames that happen to have the same frame name). The frame handle is a data structure used by an FRS to represent frames conveniently and efficiently. It is guaranteed to be unique only in a KB. The frame pretty-name is a string naming the frame in a pretty manner, that happens to be retrieved from a slot of the frame.
| Type | Unique | Unique | Frame arg? | Slot value? | Pretty? | |
| in KB? | across KBs? | |||||
| Name | symbol/ | yes | no | yes | FRS | maybe |
| string | dependent | |||||
| Handle | any | yes | no | yes | yes | no |
| Object | any | yes | no | yes | yes | no |
| Pretty-name | string | no | no | no | not portably | yes |
The frame operations are: allocate-frame-handle, coerce-to-frame, coercible-to-frame-p, copy-frame, create-frame, delete-frame, frame-in-kb-p, get-frame-details, get-frame-handle, get-frame-in-kb, get-frame-name, get-frame-pretty-name, get-frame-type, get-frames-matching, get-kb-frames, print-frame, put-frame-details, put-frame-name, put-frame-pretty-name
The class operations are: add-class-superclass, class-p, coerce-to-class, create-class, get-class-instances, get-class-subclasses, get-class-superclasses, get-kb-classes, get-kb-roots, instance-of-p, primitive-p, put-class-superclasses, remove-class-superclass, subclass-of-p, superclass-of-p
The instance operations are: add-instance-type, get-instance-types, put-instance-types, remove-instance-type, type-of-p
The operations on individuals are: coerce-to-individual, create-individual, get-kb-individuals, individual-p
The GFP knowledge model distinguishes between own slots, the slots that appear in frames, and template slots, slots that are defined in classes, but are expressed in the frames that are instances of those classes. Rather than having a different set of operations for each type of slot, GFP has a single set of operations that applies to both own and template slots. Appropriate behavior of these operations is selected by the slot-type argument that can take one of the following three values.
For an operation that does not take a slot argument, slot-type defaults to :all. For all other operations taking a slot-type argument, the default is :own.
The slot operations are: add-slot-value, attach-slot, coerce-to-slot, create-slot, delete-slot, detach-slot, follow-slot-chain, frame-has-slot-p, get-frame-slots, get-frames-with-slot-value, get-kb-slots, get-slot-type, get-slot-value, get-slot-values, get-slot-values-in-detail, member-slot-value-p, put-slot-value, put-slot-values, remove-local-slot-values, remove-slot-value, rename-slot, replace-slot-value, slot-has-value-p, slot-p
The facet operators are: add-facet-value, attach-facet, coerce-to-facet, create-facet, delete-facet, detach-facet, facet-has-value-p, facet-p, get-facet-value, get-facet-values, get-facet-values-in-detail, get-frames-with-facet-value, get-kb-facets, get-slot-facets, member-facet-value-p, put-facet-value, put-facet-values, remove-facet-value, remove-local-facet-values, rename-facet, replace-facet-value, slot-has-facet-p
Many GFP operations that return a list of results as their first value, such as get-kb-classes, provide an alternative method for accessing their results. The alternative is an enumerator, which can be used enumerate the results one at a time, or even fetch them in batches. Enumerators are especially important when operations return a large list of results; they can also provide a significant speedup in a networked environment when only a portion of the results are required. For certain programming languages, such as Java, enumerators are a very common programming idiom.
Each enumerated operation, such as get-kb-classes, has a corresponding enumerator operation. The name of the enumerator operation is derived from the basic operation by removing the prefix `get-' if any and adding the prefix `enumerate'. The arguments to the enumerator operations are exactly the same as the arguments to the basic operations.
There are a small number of operations defined on enumerators to get the next element, to determine if an enumerator has-more elements, to fetch a list of elements, to prefetch a batch of elements, and to free an enumerator. Note that in networked environments, prefetching values from the server may result in a substantial performance benefit. It is always important to free an enumerator that is not yet exhausted but which is no longer needed.
The enumerator operations are: enumerate-all-connections, enumerate-ask, enumerate-call-procedure, enumerate-class-instances, enumerate-class-subclasses, enumerate-class-superclasses, enumerate-facet-values, enumerate-facet-values-in-detail, enumerate-frame-slots, enumerate-frames-matching, enumerate-instance-types, enumerate-kb-classes, enumerate-kb-direct-children, enumerate-kb-direct-parents, enumerate-kb-facets, enumerate-kb-frames, enumerate-kb-individuals, enumerate-kb-roots, enumerate-kb-slots, enumerate-kb-types, enumerate-kbs, enumerate-kbs-of-type, enumerate-list, enumerate-slot-facets, enumerate-slot-values, enumerate-slot-values-in-detail, fetch, free, has-more, next, prefetch
Using the tell operation, a user may assert any sentence that is accepted by the tellable operation. We expect each FRS to define the classes of sentences that are tellable. To define the semantics of telling an arbitrary class of sentences is outside the scope of the current specification. We define the semantics of telling a restricted set of sentences, such that the effect of telling each sentence in this set is equivalent to executing some GFP operation. We define similar sets of sentences for the ask and untell operations. The current specification of GFP does not take any position on the semantics of using tell, ask, and untell with sentences that are not being considered here.
While defining the semantics of tell, ask, and untell, we consider the sentences that use predicate symbols of the GFP assertion language defined earlier in the knowledge model. The predicates of the GFP assertion language are class, slot and facet names in the KB, type-of, instance-of, subclass-of, primitive, template-slot-value, template-facet-value, class, individual, subclass-of, slot-of, facet-of, template-slot-of, and template-facet-of.
Table 3.3 lists the sentences, which if asserted using tell, have defined semantics according to the current GFP specification. The semantics of telling each sentence are defined using some GFP operation. In all the GFP operations, we assume that the :inference-level is :direct, and assume the default value of other parameters unless stated otherwise. For example, kb-local-only-p is assumed false, and kb is assumed to be the value returned by the GFP operation current-kb.
If we tell the sentence (person john), it will be equivalent to adding person as a type of frame john. If either person or john does not exist in the KB, the effect will be the same as the effect of executing add-instance-type when either the frame or the new type being added does not exist in the KB. The semantics of telling other sentences can be interpreted analogously.
| Sentence | Effect of Executing (tell |
| (slot frame value) | ( add-slot-value frame slot value :slot-type :own) |
| (facet slot frame value) | ( add-facet-value frame slot facet value) |
| (instance-of frame class) | ( add-instance-type frame class) |
| (type-of class frame) | ( add-instance-type frame class) |
| (primitive class) | Not a tellable sentence |
| (template-slot-value slot class value) | ( add-slot-value class slot value |
| :slot-type :template) | |
| (template-facet-value facet slot | ( add-facet-value class slot facet value |
| class value) | :slot-type :template) |
| (class class) | ( create-class class) |
| (individual individual) | ( create-individual individual) |
| (slot-of slot frame) | ( attach-slot frame slot :slot-type :own) |
| (facet-of facet slot frame) | ( attach-facet frame slot facet :slot-type :own) |
| (template-slot-of slot class) | ( attach-slot slot class :slot-type :template) |
| (template-facet-of facet slot class) | ( attach-facet class slot facet :slot-type :template) |
| (subclass-of class superclass) | ( add-class-superclass class superclass) |
| (superclass-of superclass class) | ( add-class-superclass class superclass) |
Table 3.4 lists the sentences, which if retracted using untell, have defined semantics according to the current GFP specification. The semantics of untelling each sentence are defined using some GFP operation. We assume the default value of other parameters unless stated otherwise.
If we untell the sentence (person john), it will be equivalent to removing person as a type of frame john. The semantics of telling other sentences can be interpreted analogously.
| Sentence | Effect of Executing (untell |
| (class frame) | ( remove-instance-type frame class) |
| (slot frame value) | ( remove-slot-value frame slot value :slot-type :own) |
| (facet slot frame value) | ( remove-facet-value frame slot facet value) |
| (instance-of frame class) | ( remove-instance-type frame class) |
| (type-of class frame) | ( remove-instance-type frame class) |
| (primitive class) | Not a tellable sentence |
| (template-slot-value slot | ( remove-slot-value frame slot value |
| class value) | value :slot-type :template) |
| (template-facet-value facet slot class value) | ( remove-facet-value frame slot |
| facet value :slot-type :template) | |
| (class class) | ( delete-frame class) |
| (individual individual) | ( delete-frame individual) |
| (slot-of slot frame) | ( detach-slot frame slot :slot-type :own) |
| (facet-of facet slot frame) | ( detach-facet frame slot facet :slot-type :own) |
| (template-slot-of slot class) | ( detach-slot class slot :slot-type :template) |
| (template-facet-of facet slot class) | ( detach-facet class slot facet :slot-type :template) |
| (subclass-of class superclass) | ( remove-class-superclass class superclass) |
| (superclass-of superclass class) | ( remove-class-superclass class superclass) |
Table 3.5 lists the queries, which if asked using ask, have defined semantics according to the current GFP specification. The semantics of asking each query are defined using some GFP operation. In all the GFP operations, we assume that the :inference-level is :direct, and assume the default value of other parameters unless otherwise stated.
If we ask the query (person ?x), we get all the direct instances of person, that is, it is equivalent to the result of invoking the GFP operation ( get-class-instances ?x). If we ask the query (slot ?x value), we are returned all the frames that have a slot slot containing the value, that is, it is equivalent to the GFP operation ( get-frames-with-slot-value value).
| Query | Effect of Executing (ask |
| (class ?x) | ( get-class-instances class) |
| (slot frame ?x) | ( get-slot-values frame slot) |
| (slot ?x value) | ( get-frames-with-slot-value slot value) |
| (facet slot frame ?x) | ( get-facet-value frame slot facet) |
| (facet slot ?x value) | ( get-frames-with-facet-value slot facet value) |
| (instance-of ?x class) | ( get-class-instances class) |
| (instance-of instance ?x) | ( get-instance-types instance) |
| (type-of ?x instance) | ( get-instance-types instance) |
| (type-of class ?x) | ( get-class-instances class) |
| (primitive ?x) | ( primitive-p ?x) |
| (template-slot-value slot class ?x) | ( get-slot-values class slot :slot-type :template) |
| (template-slot-value slot ?x value) | ( get-frames-with-slot-value slot value |
| :slot-type :template) | |
| (template-facet-value facet slot class ?x) | ( get-facet-values frame slot facet |
| :slot-type :template) | |
| (template-facet-value facet slot ?x value) | ( get-frames-with-facet-values slot facet value |
| :slot-type :template) | |
| (class ?x) | ( get-kb-classes) |
| (individual ?x) | ( get-kb-individuals) |
| (slot ?x) | ( get-kb-slots) |
| (facet ?x) | ( get-kb-facets) |
| (slot-of ?x class) | ( get-frame-slots class) |
| (facet-of ?x slot frame) | ( get-slot-facets frame slot) |
| (template-slot-of ?x class) | ( get-frame-slots class :slot-type :template) |
| (template-facet-of ?x slot class) | ( get-frame-facets class slot |
| :slot-type :template) | |
| (subclass-of ?x superclass) | ( get-class-subclasses superclass) |
| (subclass-of subclass ?x) | ( get-class-superclasses subclass) |
| (superclass-of ?x subclass) | ( get-class-superclasses superclass) |
| (superclass-of superclass ?x) | ( get-class-subclasses subclass) |
The tell/ask operations are: ask, askable, get-frame-sentences, tell, tellable, untell
Behaviors are discussed in Chapter 4. GFP defines the following operations on behaviors:
get-behavior-supported-values, get-behavior-values, get-kb-behaviors, member-behavior-values-p, put-behavior-values
The GFP procedure language is defined in Chapter 5. GFP defines the following operations on procedures:
call-procedure, create-procedure, get-procedure, register-procedure, unregister-procedure
Miscellaneous operations that do not fall under the preceding categories include: coerce-to-kb-value, decontextualize, eql-in-kb, equal-in-kb, equalp-in-kb, frs-independent-frame-handle, frs-name, implement-with-kb-io-syntax, value-as-string
GFP has been designed to provide access to multiple FRS on multiple servers in multiple implementation languages. The GFP operations have been specified in a way that is programming language independent. To invoke or implement these operations in a specific programming language, we must establish a binding for the programming language. Each language binding establishes language specific conventions for naming, passing arguments, returning values, data structures, and handling exceptions.
This section briefly outlines the Lisp, C, and Java bindings for GFP operations. We present enough information for a developer working in one of these languages to correctly interpret the operation definitions in Section 3.7.
The naming and argument conventions provided in the specifications will be familiar to Common Lisp programmers. They can be used directly. All GFP operations are defined in the GFP package.
Each GFP operation name is prefixed with `gfp_', and dashes are replaced by underscores. For example, the GFP operation get-slot-values is called gfp_get_slot_values.
The GFP operations are defined using mandatory positional and keyword arguments. The C programming language, however, does not provide named keyword arguments. Therefore, every argument specified for a GFP operation must be provided in the order that they appear in the operation's documentation. There is no special value to indicate that the default is to be used. All values must be explicitly provided.
All keywords that appear in the GFP specification are defined as the values of global variables prefixed by `key_'. For example, the :all keyword is the value of key_all. Because C does not allow for dynamically allocated structures to be included in compiled code or header files, the function gfp_init must be called prior to referencing any keywords at runtime.
Every GFP object is implemented as a Gfp_Node struct.
Each GFP operation returns a struct gfp_node * if it is single valued, a struct values * is returned if it is multi-valued, or void * if it returns no values.
C does not support multiple return values from functions. Operations returning multiple values return a pointer to a values struct. Functions on values include nth_value, first_value, and length.
Every GFP operation clears an error flag before executing, and sets it if an error occurs. It is the responsibility of the application to detect and handle errors. For the specific error flags, consult the header file for the C implementation.
Declarations and prototypes are defined in the file gfp.h. The function gfp_init must be called prior to executing any GFP operation or referencing any keyword at runtime.
Each GFP operation that takes a KB argument is defined as a method of the class GfpKb. Dashes are replaced by underscores, and the name is all in lower case. For example, get-slot-values would be called kb.get_slot_values(...), where kb is an instance of the class GfpKb.
Java allows for arguments to be defaulted from right to left. The Java binding provides default values for the rightmost arguments that have simple defaults. Check the method definitions in the GfpKb class to verify argument defaulting.
The Node class is the root class for all GFP objects. Subclasses of Node include Symbol, Kb, etc.
All methods implementing GFP operations return either a Node or a Values object or are void methods.
Each GFP operation returns a Node object if it is single valued, a Values object if it is multi-valued, or void if it returns no values.
Java does not support multiple return values from methods. Operations returning multiple values return a Values object, which supports the methods nthValue, firstValue, and length.
All keywords that appear in the GFP specification are defined as public static final members on the Node class. For example, the :all keyword is the value of Node._all.
Java has a full exception handling facility. All GFP conditions are implemented as Java conditions. The root of the condition graph is GfpCondition. The Java condition names are the same as GFP conditions, but the first character and the character following each dash is upper case and the dashes are removed. For example, the GFP condition constraint-violation is implemented by the Java condition class ConstraintViolation.
All GFP operations are defined here, and we list them in alphabetical order.
add-class-superclass ( class new-superclass &key kb kb-local-only-p)
Return Value(s): void
Flags: O W
Adds the new-superclass to the superclasses of class.
Returns no values.
The rationale for the arguments to this operation is as follows:
new-handle = allocate-frame-handle(name, :class, kb, handle); new-frame = create-class(name, .... :handle new-handle); new-handle == get-frame-handle(new-frame) // this identity must hold!
Reply-pattern is any list structure mentioning the variables in the query, or just the name of a variable. For example, consider a query that is a sentence of the form,
(subclass-of ?x ?y)that is, find me the things that are subclasses of other things. If there is a match in the KB for ?x = human and ?y = animal. - the class human is a subclass of the class animal - then if the reply-pattern were to be
(superclass-of ?y ?x)we would be returned a list of sentences of which one would be (superclass-of animal human). The explicit use of a reply pattern in this manner allows the user to get either sentences that can be conveniently reasserted using tell, or tuples of matches in a shape that is convenient to the application.
When error-p is true, any errors resulting from the execution of the query are signaled. When error-p is false, all possible attempts are made to continue with the query and deliver as many results as were requested.
If the resources used by ask are a concern, the time (in seconds) allowed to answer a query will be limited, if possible, as specified by timeout. If the value of timeout is false, an unlimited time is allowed for ask to complete.
The where clause can be used to specify a list of bindings to be used for any variables appearing in the query. During query evaluation, such variables are replaced by the values specified by where. A valid value of where is a list of 2-tuples, with each tuple consisting of a variable and value pair.
Ask returns three values.
The following query matches four channel oscilloscopes with a bandwidth greater than 80MHz. It returns a list of pairs (?osc ?bandwidth) satisfying the query.
(ask '(and (oscilloscope ?osc)
(number-of-channels ?osc ?chans)
(= ?chans 4)
(bandwidth ?osc ?bandwidth)
(> ?bandwidth (* 80 mega-hertz)))
:reply-pattern '(?osc ?bandwidth)
:number-of-values 10 :kb kb)
All KIF operators in the query are parsed in a
package-insensitive manner. For example, (and A B) and
(:and A B) have the same effect. Object, relation, and function constant
references in query are taken as arguments to
get-frames-matching. Frame references in the query must uniquely
identify frames. (See get-frames-matching.)
It is an error to call coerce-to-class with error-p being true, and with a value of thing that does not uniquely identify a class. If this happens, a not-unique-error error should be signaled.
Note that in some FRS, false may be a valid class. No portable program may assume that a returned value of false for the first (class) returned value implies that class-found-p is false.
It is an error to call coerce-to-facet with error-p being true, and with a value of thing that does not uniquely identify a facet. If this happens, a not-unique-error error should be signaled.
Note that in some FRS, false may be a valid facet. No portable program may assume that a returned value of false for the first (facet) returned value implies that facet-found-p is false.
For user convenience, implementors are encouraged to support the coercion into a frame of any data-structure that uniquely identifies a frame in the FRS as well as frame handles and frame objects. It is not portable to provide any value for thing other than a frame object or frame handle; get-frames-matching should be used instead.
If the :frame-names-required behavior has the value true for kb, the names of frames are always coercible to frames. If the :frame-names-required behavior is false, frame names (if defined) are not guaranteed to be coercible to frames.
This operation returns two values.
It is an error to call coerce-to-frame with error-p being true, and with a value of thing that does not uniquely identify a frame. If this happens, a not-unique-error error should be signaled.
Note that in some FRS, false may be a valid frame object. No portable program may assume that a returned value of false for the first (frame) returned value implies that frame-found-p is false.
It is an error to call coerce-to-individual with error-p being true, and with a value of thing that does not uniquely identify an individual. If this happens, a not-unique-error error should be signaled.
Note that in most FRS, false is a valid individual. No portable program may assume that a returned value of false for the first (individual) returned value implies that individual-found-p is false.
string-or-value may be one of the following.
Given a string-or-value and a target-context, returns three values.
Target-context is one of {:frame, :class, :slot, :individual, :facet, :value} and identifies the way in which the value to be extracted from string-or-value is to be interpreted.
((<<string1>> <<substring1>> <<frame1>> <<frame2>>... <<frameN>>) (<<string2>> ....) ...)where <<stringX>> are strings found in string-or-value that match the frames <<frame1>> ... <<frameN>> (possibly by using any specified wildcards), and
<<substringX>>
are the corresponding
longest matching initial substrings for each <<stringX>> (see the
specification of get-frames-matching).
(fred joe).
(coerce-to-kb-value "fr*" :frame-action :options-if-not-unique)is made, the values returned would be
It is an error to call coerce-to-slot with error-p being true, and with a value of thing that does not uniquely identify a slot. If this happens, a not-unique-error error should be signaled.
Note that in some FRS, false may be a valid slot. No portable program may assume that a returned value of false for the first (slot) returned value implies that slot-found-p is false.