Memoing for logic programs

Interpretation Next we turn to another general area of recent active research in logic programming: abstract interpretation. In the area of declarative languages, the issue of optimization is of prime importance. The most successful example of the implementation of a declarative language is the relational database query language, Structured Query Language (SQL). This is the standard language used to interact with relational databases. It is critically important for the SQL compiler to produce a near optimal way to evaluate whatever query it is given. It is only because such optimization is possible that users are willing to use SQL. For general Horn clause programs, which include recursive definitions and recursive data structures, a comparable global optimization cannot yet (and may never) be achieved. Prolog, with its depth-first backtracking search through the tree of SLD refutations, is a procedural implementation of the declarative Horn clause language. And, as a procedural programming language, it can benefit f rom the kinds o f optimization techniques developed for traditional procedural languages. To perform certain optimizations, the compiler must know certain properties of any execution of the program being processed. For example, any Prolog predicate might be called with any arbitrary set of its parameters given ground values. This multidirectionality is one source of Prolog's power. However, many Prolog predicates are called in only one direction (i.e., in one mode). I f the Prolog compiler knows the mode of a call, it can often use that information to generate more efficient code. Such information depends on global properties of the program and query. One way to gain such information would be to execute, at compile time, the program for all inputs, and then use the information obtained. This direct approach is, of course, infeasible in practice. Any interesting program will have infinitely many computations. Therefore, rather than execute the program at compile time, we "generalize" the program to make a new approximate program. The approximate program preserves the structure of the original program but operates on abstract objects (perhaps representing sets of objects) rather than the concrete objects. Then the abstract program is executed to provide approximate information about all the executions of the original program. This process is called abstract interpretation. One simple example of abstract interpretation that is familiar to everyone is the rule o f signs. Consider a program in a very simple programming language that allows a sequence of assignments, each of which assigns to a variable an expression of plus, minus and times over integers and variables. Consider, for example, the program of Figure 12. Given a set of integer values for the input variables, X and Y, executing this program will generate integer values for the output variables: U, V, W and Z. For example, initializing X = 5 and Y = 6 , the program computes Z = 3 0 , W = 900, U = 9 3 0 , and V = 870. We can extract some information that relates the inputs and outputs of the program by using the rule of signs. We abstract the integers into three sets: 1) negative integers ( ) , 2) zero (0), and 3) positive integers (+). We also denote the whole set of integers by (all). We know how the operators of plus, minus and times operate on these sets o f integers. For example, the product of two positive integers is a positive integer. The abstracted table for times