• No results found

Temporal Views on Computations

4.5 Staged Processing and Views

4.5.6 Temporal Views on Computations

Two of the main advantages of defining program execution in terms of a rewriting system are complete access to the program state and fine-grained control over the execution pro- cess. While rules can be restricted to access only certain parts of a program, as discussed in Sections 4.2.3 and 4.2.4, it is nevertheless possible to define less restricted access to the program at a meta-level. This can, for example, be used to implement optimisations or code injection as in Aspect-Oriented Programming (AOP) [102].

While a detailed view on the structure and behaviour of the program provides an ideal foundation for pattern operations, this level of detail might be inappropriate for purposes of programming or analysing a system. For example, users of a language are usually not interested in the details of internal representation. Viewing the effects of a function application does not require a view on the internals of the functions. Similarly, an aspect definition is based on join points – events in an execution model, not in the actual system implementation – and, therefore, the system must render the execution in terms of the user model.

The problem of viewing the system execution in terms of a user model can be broken down into a structural aspect (“how is an execution step visualised?”) and a temporal as- pect (“which execution steps are visible?”). Section 4.5.3 defined the structural part of the solution by defining a technique for hiding the internal representation of a program behind a syntactic interface: views. This section discusses how certain steps in the execution can be hidden.

The basic idea is to determine visibility by a pattern that is matched after every execu- tion step. That pattern pattern is connected with the external display process. If matching succeeds the system state is shown, otherwise it is hidden. The use of temporal views will be illustrated by “black-boxing” operations in the concatenative rewriting system. As defined in Section 4.2, concatenative semantics can be enforced on a generic rewriting

application strategies for transformations.

In the concatenative system that allows patterns (see Section 4.2.4), transformations that only manipulate items and operators inside quotations define a functional semantics even when all execution steps are visible. An operation such as call, on the other hand, instantiates operators outside of the quotation context. This seems to violate the concept that all operations must be functions mapping between data, i.e., between items. However, if an application of call is viewed from a “black-box” perspective, it maps data containing a quotation to data that is the result of executing the program contained in the quotation, which is functional.

For example, the program [2, 3, ⌧([swap], quotation), call] yields [3, 2]. Looking only at these two execution states, call appears to be mapping from data to data in a single step. The execution state [2, 3, swap] in between makes an implementation detail of call visible. The execution of swap again has functional semantics. The crucial point is the shift of the abstraction levels that occurs when an operator is rewritten to its implementation – in case of call a program containing operators. Seeing this implementation detail of call destroys the illusion of functional semantics.

By default, all steps of the rewriting process are visible and there is no explicit distinc- tion between “black-box” and “white-box” views on the program execution. Hiding the “internal” execution steps of an operation can be achieved by explicitly marking program state. For the concatenative system, there also needs to be a way to determine when exe- cution of the internal steps of an operation have finished and execution resumes with the next operation on the same abstraction level. Both problems can be solved by explicitly or implicitly inserting an operation hide as the right-most element of every transformation that inserts operations into the program. Operation hide has the sole purpose of control- ling “black-boxing”. The definition of hide is:

hide)⇠(✏)

For instance, the program [2, 3, ⌧([swap], quotation), call, dup] is executed from left to right. The first operation that will be executed is call. The next operation after call, dup, will be executed only after all operations that call adds to the program have finished. Plac- ing hide in front of dup makes the point of continuation (the “return address”) explicit. The first step of call’s execution produces [2, 3, swap, hide, dup]. Next, swap is executed and yields [3, 2, hide, dup]. Because there are only items before hide, this operation is executed next. The semantics of hide is to do nothing. The result is [3, 2, dup]. Finally, dupis executed and yields [3, 2, 2].

discussed above, to actually hide the states that contain the operator hide, a pattern needs to be defined. This pattern is !find hide among ↵. The pattern fails to match if there is a hide operator anywhere inside the program. By applying this technique, the step from [2, 3, ⌧ ([swap], quotation), call, dup]to [2, 3, swap, dup] is hidden and, for this reason, the system provides a functional “black-box” view on the execution of call. Together, the restriction of transformations, the definition of an application strategy, the structural views that hide the internals of operators and items and the temporal view mechanism turn the generic rewriting system into a concatenative programming system. In other words, they create the illusion of a concatenative system based on a rewriting system.