Program transformation is one of the important fields where Refal is used; both the programs to be transformed (level 1, object programs) and the transforming programs (level 2) will be typically written in Refal -- this makes self-application of transforming programs possible.
To write Refal programs that deal with Refal programs, we have to represent object programs by some object expressions because free variables and activation brackets which are used in object programs cannot be objects of transformation in Refal. Indeed, suppose that we have some program and want to transform every call of a function F1 in it into the call of F1 . A sentence like:
Subst21 { e.1 <F1 e.X> e.2 = e.1 <F2 e.X> <Subst21 e.2>; }will not work. According to the syntax of Refal, active sub-expressions cannot be used in the left side. But even if we extended Refal to allow such a use, the active sub-expression <F2 e.1> in the right side would be understood as a process, not an object; the evaluation of this call would start before the further evaluation of Subst21 , even though we did not want it. Likewise, we cannot use a free variable as an object of transformation, because it will be understood by the Refal machine as a signal for substitution.
A mapping of all Refal objects on a subset of object expressions will be
referred to as a metacode. Such a mapping must, of course,
be injective; i.e., the images (``the metacodes'') of distinct Refal
objects must be distinct, so that
there is a unique inverse transformation. Metacode
transformation is the lowering of the metasystem level of the object --
from a controlling device to an object of work.
Therefore, when we apply a
metacode transformation to an expression
, we say that we downgrade
it to the metacode; applying the inverse transformation
we say that we upgrade
from the metacode.
When we actually write Refal programs dealing
with Refal programs, we must choose a certain concrete metacode
transformation. But when we speak about downgrading and
upgrading expressions, we often want a notation allowing us to leave
these transformations unspecified. Thus downgrading to the metacode
will be represented by a
down arrow
; for upgrading we reserve the up arrow
.
When the range of these operations extends to the end of the current
sub-expression, we simply put
or
in front
of it. If it is necessary to delimit the range, we use braces.
For example, the concatenation of the downgraded
1 with the
unchanged
2 is {
1}
2,
while (
1
)
2
is the same as ({
1})
2.
Obviously,
=
; and if
exists,
then
=
.
The purpose of metacoding is to map activation brackets and
free variables on object expressions. It would be nice to leave
object expressions unchanged in the metacode transformation.
Unfortunately this is impossible
because of the requirement of a unique inverse transformation.
Indeed, suppose that we have such a
metacode. Then
e.1
must be equal to
e.1
,
because
e.1
is an object
expression. It follows that two different objects, e.1
and
e.1
,
have identical metacodes.
We can, however, minimize the difference between an object expression and its metacode. The metacode we are using in the Refal system singles out one symbol, namely the asterisk '*' , which is changed in the metacode transformation. All other symbols and object brackets (parentheses) are represented by themselves. The following table defines our metacode:
Expression ![]() | its metacode ![]() ![]() |
s.I | '*S'I |
t.I | '*T'I |
e.I | '*E'I |
< F ![]() | '*'(( F) ![]() ![]() |
(![]() | (![]() ![]() |
![]() ![]() | {![]() ![]() ![]() ![]() |
'*' | '*V' |
S (but not '*') | S |
Thus
e.X
is '*E'X
,
s.1
is '*S'1
,
'a*b'
is 'a*Vb'
,
<F e.Arg>
is '*'((F)'*E'Arg)
etc.
When the metacode transformation is applied to an object expression, its result can be computed by calling the Refal function Dn which replaces every '*' by '*V' :
Dn { '*'e.1 = '*V' <Dn e.1>; s.2 e.1 = s.2 <Dn e.1>; (e.2)e.1 = (<Dn e.2>) <Dn e.1>; = ; }The function Dn is also implemented in the Refal system as a built-in function which downgrades its argument in one step.
For any object expression 0,
<Dn0> ==
0
The time required for downgrading or upgrading an object expression is proportional to its length. It is often desirable to delay the actual metacoding of an expression until the moment when its downgraded form is actually used because it well may happen that the whole expression, or its part, will be later upgraded back to its original self. Therefore, we may avoid an unnecessary two-way transformation of an expression if we somehow indicate that the expression must be put in the metacode but do not actually transform it. We use the expression
'*!'(to represent the delayed metacode of0)
'*!'() is equivalent to <Dn
>
Indeed, even if includes free variables and active sub-expressions,
the implied sequence of actions on both sides of this equivalence
is the same: first free variables must be replaced by object expressions,
then the computation process must start and end, and then the result
must be downgraded.
The inverse function of Dn
is Up
. If its argument is
the result of a metacode transformation of an object expression 0,
then Up
returns
0:
<Up <DnBut we can extend the domain of Up to include metacodes of any ground expression0>> ==
0
<Up '*'((F)'abc')> == <F 'abc'>which will immediately start a computation process. Generally, for any ground expression
<Upgr> ==
gr
The function Up can be defined in Refal as follows:
Up { '*V'e.1 = '*'<Up e.1>; '*'((s.F) e.1)e.2 = <Mu s.F <Up e.1>> <Up e.2>; '*!'(e.2)e.1 = e.2 <Up e.1>; s.2 e.1 = s.2 <Up e.1>; (e.2)e.1 = (<Up e.2>)<Up e.1>; = ; }
The evaluation of <Up gr>
is a simulation of the evaluation
of
gr -- as one can see from the definition of Up
.
This function converts the ``frozen'' function calls in
gr into
their active form, and does this in exactly the same inside-out order as
the Refal machine would do.
Exercise 6.2
If the the function Up
meets the first metacode of a free variable,
e.g., '*E'X
, it will leave it unchanged, and that would be an error.
When its argument is not a metacode of a ground expression,
this function must be undefined. Indeed, the upgrading of '*E'X
should result in the free variable e.X
which will be put
in the view field; however, this is not allowed by the definition of the
Refal machine. Modify the above definition of Up
so that
when the argument is out of the domain, an error message is printed
and an abnormal stop occurs.
Actually, the above definition of the function Up
is of a purely academic interest because the Refal system
includes the built-in function Up
which
is equivalent to our Refal definition for an argument gr,
but works faster. In one step it puts
gr in the view field
of the Refal machine.
In addition, its domain is extended to include the metacode of
any Refal expression, not necessarily a ground one.
This will be discussed in Sec. 6.4.
Using the function Up , we have the same alternatives, in terms of modular structure, as with Mu . Our built-in function Up is static. The user must see to it that the functions intended for activation by Up are visible from the module in which the call of Up occurs. If your function using Up is defined in an auxiliary module, replace it by UpD and define UpD in the main module:
$ENTRY UpD { e.X = <Up e.X> }in a full analogy with the treatment of Mu .
(a)Exercise 6.4 Compute:<Add (35)16> (b)
<Up (
<F (e1)e2>)'*!'(e3)> (c)
'*'((Comp) 'A*VB') (d)
'*!'('A*B')
(a) <Dn <Add (35)16>> (b) <Up '*!'('A*B')>