#6. Failures and restoring. The following facility is provided for testing alternative ways to solve a task. At some operations one may refer to successful or unsuccessful outcome and, correspondingly, different continuations (transferring control to different instructions). The most interesting case is a failure at calling a procedure which can be caused by some operation inside the procedure. This operation can be either a special primitive provided to announce failure (traditionally written as FAIL) or an unsuccessful call to a procedure (as well as using a package, setting an attribute, or merging two vertices) if the latter operation contained no reference to a possibility of failure. Such an operation results in termination of the containing procedure and unsuccessful outcome of its call; if at the latter call no reference to failure has been made either, it leads to unsuccessful termination of the further containing procedure, and so on. It is only when we reach a procedure call (package use) with explicitly specified response to failure, that this response is performed and the procedure containing this last call continues normally. (If none of the outer calls has made a provision for failure the whole system terminates abnormally).

The response to a failure of an operation includes the following actions:

the state of the net is restored to the moment of the beginning of the operation (i.e., arcs drawn after that moment are deleted, removed arcs are restored, merged nodes are unmerged; new nodes and fetched modules are not deleted but their connections with nodes that existed before are destroyed);

the control is passed to the label (in the procedure) that has been specified for this situation.

Thus, to try out a way of solving a task, one should describe this way in the form of a procedure and then call the procedure specifying a jump to an alternative variant for the case of failure. This alternative variant will be run from the same state of the net as for the first variant. Using logical terms we can say that we make a hypothesis (indicating that this is just a hypothesis rather than definite assertion), and then infer some conclusions. If we ultimately arrive at a failure (an unacceptable conclusion), we give up all intermediate conclusions as well as the hypothesis itself, and instead accept a conclusion (this time unconditionally) that the hypothesis is unacceptable.

Announcing a failure one can give some additional information of the nature of the failure in the form of some net vertex. At the termination of the failing call that vertex will be assigned to a specially indicated variable (if any). But this method cannot be used to transmit more detailed information for, if one builds a node with attributes and attempts to pass it across the fail, the caller will receive only that node because its attributes will be deleted while the original state of the net is restored. There is a variant of FAIL without restoring the net. Another method of passing information about failure depends on the fact that restoring does not affect arcs issuing from atoms (only state of nodes is restored).

There is one more facility at announcing a failure. A call identifier may be specified, to designate a call at which the failure is to be intercepted. If a call has not explicitly specified the same identifier as the current FAIL operation, it is treated as if it had no provisions for failure, and the failure propagates outward until a call with the given identifier is found.

The described mechanism, on one hand, resembles backtracking in PLANNER-like languages for AI, on the other hand, it is similar to exception handling in ADA or abnormal termination processing in OS/360. But ADA, as well as OS/360, do not restore automatically the original memory state. As compared to PLANNER, this mechanism is substantially weaker. In this system the original state can be restored only before the completion of a procedure whose call was marked as potentially failing; unlike PLANNER, one cannot make a choice inside a procedure, then complete it, and on a later failure retry from the point of the choice. For some applications more powerful PLANNER-like mechanism would be desirable.

The mechanism of failures and restoring is also used by the system itself at some operations looking as primitives. Consider one such case, viz., finding virtual attributes. In the operation of getting an attribute (walking along an arc) additional actions may be specified to return a result even if the arc with the given name is missing. One kind of such action is making an arc with the given name leading to a newly created node. Another possibility is to specify a procedure or a set of procedures (in the form of a node with unnamed arcs). The system calls these procedures passing to them the original node as the parameter. If all the calls fail the operation of finding the attribute is also regarded as unsuccessful, but if one of the calls completes successfully its result is returned as the value of the attribute and the remaining calls are skipped. There is also a way to avoid explicit specification of the procedures at the operation of finding the attribute. The procedures can be attached to the name of the wanted arc (i.e., to the corresponding atom) as an attribute with a special name (#GETATTR in this implementation). By assigning a #GETATTR attribute to an atom one instructs the system that when an attribute is sought for with the name of this atom the absence of an explicit attribute must be compensated by calling the given set of procedures (or a single procedure).

To illustrate the last feature consider the case when for net nodes representing people one has specified "year of birth" attribute and has failed to specify any "age" attribute. The procedure for computation of age from year of birth can be attached to the "age" atom, and then each attempt to find the age by seeking an attribute of this name will return a correct result, and whoever writes a procedure seeking the "age" attribute does not need to know that it is not a "real" attribute. Another example: nodes may represent numerical quantities from some task and sometimes have attributes named "value" and "methods of computation". The atom for "value" can have a procedure that instructs in the absence of an explicit value to use the attribute "methods of computation" and to perform the attached procedures (until the first success).

These two examples can be regarded as a method of representing in this system relationships between notions "age" and "year of birth" (respectively, "value" and "methods of computation"). It differs from the method of representing relationships between notions described in #5 in that here attribute names are considered rather than classes. Automatic assignment of attributes to the atoms is achieved by inclusion in the library of appropriate modules named like those atoms.

The implementation of virtual attribute computation has a protection against infinite recursion; if at the time of computing an attribute of some vertex with some name using some procedure or set of procedures an attempt is taken to compute an attribute of the same vertex with the same name using the same procedures this attempt fails immediately. This check is similar to a check for self-embedding at unification of terms in PROLOG.