• Aucun résultat trouvé

Partial Evaluation and Reactive Systems

Partial Evaluation

9.5 Partial Evaluation and Reactive Systems

A GDC program may in fact be represented by an AND-OR tree, with the OR-arcs labeled as above and the AND-arcs labeled with the argument assignments. For instance, the AND-OR graph in Figure 9.5.1.

A B

B:=[]

L P S G

S:=[] G:=[]

X Y Z

Z:=Y A=[]

A=[P|L]

A:=S B:=SS

A:=G B:=SG

L=[]

L=[H|T]

H>P

G:=[H|G1]

L:=T G:=G1 L=[H|T]

H=<P L:=T S:=S1

S:=[H|S1]

X:=SS Y:=[H|SG]

Z:=B

Z:=[H|W]

X=[] X=[H|T]

X:=T Z:=W

Fig. 9.5.1 AND-OR representation of quicksort

represents the GDC quick-sort program:

qsort([],B) :- B=[].

qsort([P|L],B)

:- part(L,P,S,G), append(SS,[H|SG],B), qsort(S,SS), qsort(G,GG).

part([],P,S,G) :- S=[], G=[].

part([H|T],P,S,G) :- H=<P | part(T,P,S1,G), S=[H|S1].

part([H|T],P,S,G) :- H>P | part(T,P,S,G1), G=[H|G1].

append([],Y,Z) :- Z=Y.

append([H|T],Y,Z) :- append(T,Y,W), Z=[H|W].

The small round nodes in Figure 9.5.1 are the AND-nodes and the rectangular leaves represent system primitives. The node labeled with the variable arguments A B represents the qsort actor, the node labeled L P S G the part actor and the one labeled X Y Z the append actor.

Partial evaluation may proceed in a similar way to that described with the purely OR-graphs used previously. However, a problem occurs when considering individual actors in isolation from the context in which they occur. Evaluation of one actor may bind variables in its siblings. To partially evaluate an actor without taking account of bindings made by siblings means that a large amount of unnecessary evaluation is done. Partial evaluation takes account of every possible value allowed for in the behaviors for the actor rather than the limited set of values that its context dictates. It is for this reason that in full evaluation of Prolog only the leftmost goal is expanded, with the trust that the programmer has arranged the goal order so as to provide maximum benefit from one goal constraining its siblings. In GDC only those actors that are sufficiently bound so as not to require any information from their siblings in order to commit to a clause are expanded. In partial evaluation a technique is often used to limit partial evaluation of components occurring in some context to the finite set of values which some dynamic variable may have in that context, treating it as static for each of its possible values. This technique is so common that in their survey work on partial evaluation, Jones, Gomard and Sestoft [1993] refer to it simply as the trick.

One way of dealing with this issue in partial deduction is to use a method that is intermediate between having nodes representing full computation states and nodes representing individual actors. AND-OR trees are used, but an OR-node may represent a group of more than one actor. This is done in particular in cases where one actor is dependent on another. That is, its reduction depends on the value of some variable local to the behavior in which it occurs which is set by another actor in that behavior. If a0 is the actor which sets the variable V and a1 is the actor whose rewriting depends on the value of V, then a node representing a0&a1 can be set up.

In GDC, since a1 is dependent on a0, it cannot rewrite until a0 has committed to a behavior and reduced. So the a0&a1 combination can only alter through a0 reduction. The OR-arcs descending from it are therefore labeled only with the channel values required for a0 to commit and the descendants from the AND-node include a1 (possibly with further channel bindings as it may contain other shared channels with a0 which need to be bound for a0 to commit). The a1 actor may again be combined in a single node with other actors and any further combination may be checked for correspondence and hence node merger with the original a0&a1 combination. On recreating the GDC program from the graph, the a0&a1 combination will become a single actor. The result is a form of goal fusion [Furukawa and Ueda, 1985]. This will be discussed in further detail and more formally below in Section 9.7.

Partial evaluation has been described as a transformation of some program P with respect to some arguments a1,…ak, to give a residual program P´ such that the result of executing P´(ik+1,…in) is the same as executing P(a1, … , ak,ik+1,… , in) for any ik+1,… , in. It has already been mentioned that this disregards the case that there is no

necessity that the specialization is with respect only to the first k arguments to P rather than any selection of k arguments. Another simplification in this explanation is that it ignores the possibility that any of the ai may be a compound term which itself contains further dynamic variables. This is of particular importance in the partial evaluation of meta-interpreters. Generally an actor of the form

:- reduce(Actor,Prog,InfoIn,InfoOut)

is partially evaluated by binding Actor to some tuple f(v1, … ,vn) where v1, … ,vn are variables and Prog to some ground representation of a program (InfoIn and InfoOut being optional additional information input and output from the meta-interpreter), resulting in an actor reduce_f(v1, … ,vn,InfoOut) together with a program for reduce_f. For GDC, partial evaluation may be seen as “flattening out”

the arguments of an actor, returning an actor and a partially evaluated program for it whose arguments are just the channels that occurred in the original argument list, less the constants and any surrounding functors.

Recall that an actor in a reactive system is a program execution which “reacts” when it receives a message or partial message. The actor reacts by reducing itself as far as it can to the point where it is suspended waiting for messages (which may include the further channels). It can be seen that this activity of transformation to a new process following the reception of input (but not enough input to fully bind all input channels) is almost the same as partial evaluation. The difference is that partial evaluation stores the result of the transformation in response to partial input so that instead of being used just once in response to the next partial message, it may be re-used any number of times.

To some extent the program evaluator for any reactive system language is a partial evaluator. In the parallel functional language Multilisp [Halsted, 1985], for example, futures are first-class entities in the language which represent locations into which computations are concurrently putting results. A computation in Multilisp may proceed putting futures into compound structures and suspending only when the actual value that is stored in a future is required. Suppose a Multilisp computation were run with no concurrent computation putting results into some futures it took as input. A stored version of the program as it finally suspended waiting for the futures to be evaluated could be regarded as a partial evaluation in which the futures are the dynamic variables.

The difference between partial evaluation and general evaluation in a reactive system is that as the result of partial evaluation is to be saved for repeated future use, it is worthwhile spending a fair amount of time on it. In the normal evaluation mechanism in a reactive system it would generally not be cost-effective to spend large amounts of processor time fully transforming process code in response to its latest input.

However, one could imagine in a large-scale distributed system that a processor which would otherwise be idle could spend its time partially evaluating an actor which is waiting for further mesages, with respect to the messages that it has already received.