More precisely, Wave's composition and transformation functions do not
commute.  Transformation of the composition of two ops does not always
produce the same op as the successive transformations of those two ops:

Ts (Ts (s, c1), c2)  !=  Ts (s, c1 ; c2)

where Ts is one side of the transform function (i.e., the side that produces
the transformed server op), s is a server op, and c1, c2 are client ops.

This is why in Wave there are restrictions on when composition can be
performed.  Basically, composition only ever occurs in the client, in its
queue of outgoing ops, before any transform is done.  The server does not
compose ops.

If you're wondering what it would take to make them commute, I do not have a
good answer.  I do not know whether the obvious transform and composition
functions for more trivial data types usually commute or not, but I suspect
that they would.

-Dave

On Mon, Mar 7, 2011 at 3:11 AM, Daniel Danilatos <danila...@google.com>wrote:

> Yes, compose and transform are not compatible with eachother.
>
> Dan
>
> Στις 5 Μαρτίου 2011 12:45 π.μ., ο χρήστης Joseph Gentle
> <jose...@gmail.com>έγραψε:
>
> > 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);
> >
>

Reply via email to