I've just found a surprising bug of sorts in my OT code. It turns out
compose has peculiar side effects in terms of information loss / gain.
Imagine you have a compose function '+', infix transform function 'T',
server op 's' and client ops 'c1' and 'c2', its possible that:
s T c1 T c2 != s T (c1 + c2)
Wave's concurrency control algorithms never do this, so its not a
problem in practice. Is this normal? It never occurred to me that this
would be the case...
If anyone is curious, I've reproduced this behaviour using WIAB's
docops (below).
-J
... Basically, it happens if
s = insert: 's', skip: 1
c1 = delete: 'x'
c2 = insert: 'c'
// Make println() usable.
DocOpScrub.setShouldScrubByDefault(false);
DocOp s = new DocOpBuilder().characters("s").retain(1).build();
DocOp c1 = new DocOpBuilder().deleteCharacters("x").build();
DocOp c2 = new DocOpBuilder().characters("c").build();
DocOp cc = Composer.compose(c1, c2);
// s_ = s T c1
DocOp s_ = Transformer.transform(c1, s).serverOp();
// s__ = s T c1 T c2
DocOp s__ = Transformer.transform(c2, s_).serverOp();
// cc = c1 + c2
System.out.println("cc: " + cc);
// scc_ = s T (c1 + c2)
DocOp scc_ = Transformer.transform(cc, s).serverOp();
// s__ and scc_ are different!
System.out.println("s__: " + s__);
System.out.println("scc_: " + scc_);
System.out.println();
// ... And not just different syntactically. They're different semantically.
DocOp doc1 = Composer.compose(ImmutableList.of(new
DocOpBuilder().characters("x").build(), c1, c2, s__));
System.out.println("doc1: " + doc1);
DocOp doc2 = Composer.compose(ImmutableList.of(new
DocOpBuilder().characters("x").build(), cc, scc_));
System.out.println("doc2: " + doc2);