In program transformation, metacoded function calls of level 1 (which usually include some free variables) become arguments of level 2 functions:
<F2 ...i.e.,<F1 ... e.1 ...> ... >
<F2 ... '*'((F1) ... '*E'1 ...) ... >This is a schematic representation of the situation. In the argument of F2 we may have any combination of function calls as well as passive expressions; we refer to these combinations as configurations of the Refal machine loaded with the definitions of level 1 functions.
The basis of any system of program transformation in such languages as Refal is the replacement of a function call by what comes from it in one step of the algorithm. When doing program transformation in Refal, we compare the call with the left sides of the defining sentences which, like functional configurations, are used in a metacoded form. Then we simulate a step of the Refal machine. This process is known as driving. In driving we split the sets of possible values of free variables into subsets corresponding to different sentences of the function definition. This makes it possible for each subset to perform the step in a unique way. Suppose, for instance, that we have the following definition of a function Fa :
Fa { 'a's.X e.1 = 'b's.X <Fa e.1>; s.2 e.1 = s.2 <Fa e.1>; = ; }and drive the call <Fa e.1> . The result of one step of driving is a graph where directed edges designate some subsets of the full set of the variables' possible values, while the nodes represent the configurations resulting from the step. In our case the graph will have three edges (branches). The first branch defines the step for the instance when the value of e.1 starts with 'a' followed by some symbol s.X . It leads to the configuration:
'b's.X <Fa e.1>where e.1 now stands for what remains of the original e.1 after the first two symbols are stripped from it. The second branch corresponds to the subset where the first symbol is not 'a' -- or it is 'a' but the remaining part of the expression is either empty or starts with a parentheses. The third subset consists of one element: the empty expression.
It is clear that driving, like any form of interpretation, takes much more time than direct computation. Therefore, when there are no free variables in a configuration, it would be much faster to directly compute it than to use the general mechanism of driving. Moreover, whenever the step can be performed in a unique way, independent of the values of free variables, it would also be very desirable to perform it by a direct computation. Suppose, e.g.,, that with the definition above we have this configuration:
<Fa 'a's.1'bc'e.2>No matter what the values of s.1 and e.2 are, the resulting configuration will be:
'b's.1 <Fa 'bc'e.2>In a similar way, the next two steps will be unique and result in:
'b's.1'bc' <Fa e.2>It is only now that the next step depends on the values of the variables, and driving is inevitable. Thus, in some cases driving can be reduced to a direct partial evaluation of function calls.
The Refal system provides the means for an efficient partial evaluation as long as a direct execution of Refal steps does not depend on the values of free variables. This is achieved as follows.
An additional type of object is allowed in the view field. These objects will be referred to as unknown. The information content of an unknown is its type (S , T or E ), its level (a non-negative whole number), and its index (a macrodigit). The system knows that an unknown of the s-type stands for a symbol and an unknown of the t-type for a term; this is taken into account in matching. As long as the values of unknown objects are not required for execution of a Refal step, they are passed from one expression to another and may be copied. Their existence, or rather their difference from normal object expressions, is not noticed by the system. Unknown objects can been seen by using the tracer, which prints them out in the form:
\type.level index
Unknown objects are created by the function Up and transformed by Up and Dn . If we denote an unknown object of type t, level n, and index i as:
unknown(t,n,i)then the extension of these functions' definitions can be described by the following formulas:
<Up '*'s.T s.I> = unknown(s.T,0,s.I); <Up unknown(t,n,i)> = unknown(t,n+1,i); <Dn unknown(s.T,0,s.I)> = '*'s.T s.I; <Dn unknown(t,n+1,i)> = unknown(t,n,i);
The built-in function Ev-met (`evaluate over metacode') is available. Its effect on the view field is as if it were defined as follows:
Ev-met { e.X = <Freezer <Up e.X>>;}In addition it does some system operations. Freezer is a fictitious function which we need in order to enclose the expression resulting from upgrading and executing the argument of Ev-met . It also ``freezes'' its argument, i.e., downgrades it back into metacode. Freezer cannot be called by the user and appears only when placed by Ev-met . After the argument of Ev-met is upgraded, the process of direct execution starts. It may end in the following three ways:
0![]()
1![]()
1![]()
2![]()
Note that even though unknown objects essentially stand for the same things as free variables, we had to introduce a special notation for them in order to express the operations over them as Refal sentences. Indeed, something like:
<Up '*E'X> = e.Xwould violate the syntax of Refal (a free variable in the right side which is not found in the left side), while:
<Dn e.X> = '*E'Xwould define Dn as a completely different function.
Function Up should be used only when we know that its argument does not include free variables. If it does, Up will create unknown objects and since they are not enclosed by the freezer, an error will occur the first time an unknown variable hinders the evaluation. Note that the first thing most built-in functions do is to transform their arguments from the list structure into arrays. Therefore, they cause freezing even before starting their specific work.
Unknowns cannot be dealt with in the same way as all other legitimate (``known'') Refal objects. Their only purpose is to make partial evaluation faster. Whatever you want to do with them, you must first convert all expressions containing unknown objects into metacode. Then unknowns become metacoded representations of free variables.
Let us draw a sketch of the use of the freezer for program transformation. Suppose we have a (metacoded) call of the function Fa to explore:
'*'((Fa)where)
Try-pe { e.E = <Checkfr <Ev-met e.E>> } * Check the contents of freezer Checkfr { 0 e.E = <Passive e.E>; 1 e.E = <Drive e.E>; 2 e.E = <Undefined e.E>; }When we call:
<Try-pe '*'((Fa)the call of Fa is upgraded and put in the freezer. Suppose)>
as in the example above, then three steps of the Refal machine will be performed directly, after which the driving will take over. If= 'a*S'1 'bc*E'2