14.6 Context relations and transformation variants
Causality and concurrency
O1 --> O2means thatO1had already been executed whenO2was generated.O1 || O2means that neitherO1 --> O2norO2 --> O1; the operations are concurrent.
Input and output states
Let in(O) be the state on which operation O is executed, and let
out(O) be the state obtained after its execution. If S=in(O), then
S+O denotes out(O).
Two operations may have the following context relations:
- context equivalent, written
O1 <> O2, whenin(O1)=in(O2); - context convergent, written
O1 >< O2, whenout(O1)=out(O2); - context serialised, written
O1 >> O2, whenout(O1)=in(O2); - equivalent, written
O1 ~ O2, when bothO1 <> O2andO1 >< O2.
These relations are contracts on the caller. The algorithms do not store states or test the relations at run time.
IT, IT2, ET and ET2
The four transformation variants move an operation between the edges of one transformation
diamond. Start with concurrent, context-equivalent operations O1 and O2.
Inclusion transformation produces O1'=IT(O1,O2) and
O2'=IT(O2,O1):
IT- Transforms one of two context-equivalent operations forward past the other.
IT2- Transforms the first operation of a context-serialised pair forward so that it converges with the second.
ET- Transforms the second operation of a context-serialised pair backward so that it becomes context equivalent to the first.
ET2- Transforms one of two context-convergent operations backward so that it precedes the other.
The eight directions around the diamond
| Precondition | Transformation | Postcondition |
|---|---|---|
O2 <> O1 | IT(O2,O1) = O2' | O1 >> O2' |
O1 <> O2 | IT(O1,O2) = O1' | O2 >> O1' |
O2 >> O1' | IT2(O2,O1') = O2' | O1' >< O2' |
O1 >> O2' | IT2(O1,O2') = O1' | O2' >< O1' |
O2 >> O1' | ET(O1',O2) = O1 | O1 <> O2 |
O1 >> O2' | ET(O2',O1) = O2 | O2 <> O1 |
O1' >< O2' | ET2(O2',O1') = O2 | O2 >> O1' |
O2' >< O1' | ET2(O1',O2') = O1 | O1 >> O2' |
Information-preserving merge
An information-preserving merge is compatible with IT and ET. It is not sufficient for the merged operation merely to have the same immediate effect as the original sequence; it must behave as that sequence in later transformations.
Given context-serialised operations [O1,O2], define O1 ⊕ O2 as the
merge of O1 and O2 into a single composite operation.
We require the following properties:
- MP1
- (O1 ⊕ O2) ⊕ O3 = O1 ⊕ (O2 ⊕ O3) (associativity)
- MP2
- S + [O1 O2] = S + (O1 ⊕ O2)
- MP3
- IT(O1, O2 ⊕ O3) = IT(O1, [O2, O3])
- MP4
- IT(O1 ⊕ O2, O3) = IT(O1,O3) ⊕ IT(O2, IT(O3, O1))
In-place functions
DualIT, Transpose and Merge are in-place functions.
DualIT(O1,O2)- Given
O1 <> O2, mutate both arguments so thatO1'=IT(O1,O2)andO2'=IT(O2,O1), for whichO1 >> O2',O2 >> O1'andO2' >< O1'. Transpose(O1,O2)- Given
O1 >> O2, mutate both arguments to replace the sequence[O1,O2]by[O2',O1'], such thatO2' >> O1'and applying either sequence has the same result. Merge(O1,O2)- Given
O1 >> O2, computeO1 ⊕ O2in place, replacingO1by the composite operation and consumingO2.
The first two functions could instead be implemented using pure IT and ET functions that construct and return new operations:
void DualIT(Operation& O1, Operation& O2)
{
Operation newO1 = IT(O1, O2);
Operation newO2 = IT(O2, O1);
O1 = newO1;
O2 = newO2;
}
void Transpose(Operation& O1, Operation& O2)
{
Operation newO2 = ET(O2, O1);
Operation newO1 = IT(O1, newO2);
O1 = newO1;
O2 = newO2;
}
Direct implementations of DualIT and Transpose can also use a more efficient
decomposition than separate calls to IT and ET. They can process a corresponding pair of components
once and update both operations from the result. Transpose can likewise update both sides
during one coordinated decomposition instead of materialising the ET result and traversing it again
for IT. For example, when IT shifts text insertion positions to the right, DualIT needs
to compare the insertion positions only once. If the positions are equal, it needs to compare the
site identifiers only once. Two separate IT calls would repeat those comparisons while computing
the two sides of the same transformation diamond.
The definitions above preserve the void, in-place interfaces of DualIT and
Transpose, but the pure transformations construct complete result operations before
assigning them back to the arguments. For substantial operation data structures, constructing
those results can require considerable copying and cause excessive heap allocations. Direct
in-place implementations can share work and reuse existing storage, which can provide better
performance.