diff --git a/collection/shared/src/main/scala/hokko/collection/DeltaADT.scala b/collection/shared/src/main/scala/hokko/collection/DeltaADT.scala index 4122d07..79f054c 100644 --- a/collection/shared/src/main/scala/hokko/collection/DeltaADT.scala +++ b/collection/shared/src/main/scala/hokko/collection/DeltaADT.scala @@ -12,6 +12,7 @@ import scala.collection.{ } object Delta { + def combineDelta[El, Coll](d1: Delta[El, Coll], d2: Delta[El, Coll]): Delta[El, Coll] = (d1, d2) match { @@ -39,10 +40,10 @@ object Delta { case Id() => acc case Combined(d1, d2) => applyFoldUndo(applyFoldUndo(acc, d1, op, undo), d2, op, undo) - case Concat(others) => others.foldLeft(acc)(op) - case p @ Patch(_, patch, _, removed) => - val accWithNewElements = patch.foldLeft(acc)(op) - removed.foldLeft(accWithNewElements)(undo) + case Concat(others) => others.foldLeft(acc)(op) + case u @ Updated(_, previous, next) => undo(op(acc, next), previous) + case p @ Prepend(el) => op(acc, el) + case a @ Append(el) => op(acc, el) } } @@ -50,20 +51,13 @@ object Delta { implicit cbfMap: CanBuildFrom[Repr[A], B, Repr[B]], cbfDelta: CanBuildFrom[Repr[B], B, Repr[B]]): Delta[B, Repr[B]] = delta match { - case Id() => Id() - case Combined(d1, d2) => combineDelta(map(d1, f), map(d2, f)) - case c @ Concat(_) => c.map(f) - case p @ Patch(_, _, _, _) => p.map(f) + case Id() => Id() + case Combined(d1, d2) => combineDelta(map(d1, f), map(d2, f)) + case c @ Concat(_) => c.map(f) + case u @ Updated(_, _, _) => u.map(f) + case p @ Prepend(_) => p.map(f) + case a @ Append(_) => a.map(f) } - -// def filter[A, Repr[_]](delta: Delta[A, Repr[A]], -// p: A => Boolean): Delta[A, Repr[A]] = -// delta match { -// case Id() => Id() -// case Combined(d1, d2) => combineDelta(filter(d1, p), filter(d2, p)) -// case c @ Concat(_) => c.filter(p) -// case p @ Patch(_, _, _, _) => p.filter(p) -// } } sealed trait Delta[+El, Coll] { @@ -95,80 +89,42 @@ case class Concat[A0, Repr[A] <: TraversableLike[A, Repr[A]]]( def map[B](f: A0 => B)( implicit cbfMap: CanBuildFrom[Repr[B], B, Repr[B]]): Concat[B, Repr] = Concat(that.map(f)) +} + +case class Append[A0, Repr[A] <: SeqLike[A, Repr[A]]](el: A0)( + implicit canBuildFrom: CanBuildFrom[Repr[A0], A0, Repr[A0]] +) extends Delta[A0, Repr[A0]] { + def apply(c: Repr[A0]): Repr[A0] = c :+ el + def mapIndex(f: (Int) => Int): Delta[A0, Repr[A0]] = ??? + + def map[B](f: A0 => B)( + implicit cbfMap: CanBuildFrom[Repr[B], B, Repr[B]]): Append[B, Repr] = + Append(f(el)) +} -// def filter[B](p: A0 => B)(implicit cbfMap: CanBuildFrom[Repr[B], B, Repr[B]]) -// : Delta[B, Repr[B]] = { -// val filtered = that.filter(p) -// if (filtered.isEmpty) Id() -// else Concat(filtered) -// } +case class Prepend[A0, Repr[A] <: SeqLike[A, Repr[A]]](el: A0)( + implicit canBuildFrom: CanBuildFrom[Repr[A0], A0, Repr[A0]] +) extends Delta[A0, Repr[A0]] { + def apply(c: Repr[A0]): Repr[A0] = el +: c + def mapIndex(f: (Int) => Int): Delta[A0, Repr[A0]] = ??? + def map[B](f: A0 => B)( + implicit cbfMap: CanBuildFrom[Repr[B], B, Repr[B]]): Prepend[B, Repr] = + Prepend(f(el)) } -case class Patch[A0, Repr[A] <: SeqLike[A, Repr[A]]]( - from: Int, - patch: GenSeq[A0], - replaced: Int, - removedElements: TraversableOnce[A0])( +case class Updated[A0, Repr[A] <: SeqLike[A, Repr[A]]](index: Int, + previous: A0, + next: A0)( implicit cbf: CanBuildFrom[Repr[A0], A0, Repr[A0]] ) extends Delta[A0, Repr[A0]] { override def apply(c: Repr[A0]): Repr[A0] = - c.patch(from, patch, replaced) + c.updated(index, next) - override def mapIndex(f: (Int) => Int) = this.copy(from = f(this.from)) + override def mapIndex(f: (Int) => Int) = this.copy(index = f(this.index)) def map[B](f: A0 => B)( - implicit cbfMap: CanBuildFrom[Repr[B], B, Repr[B]]): Patch[B, Repr] = { - Patch(from, patch.map(f), replaced, removedElements.map(f)) + implicit cbfMap: CanBuildFrom[Repr[B], B, Repr[B]]): Updated[B, Repr] = { + Updated(index, f(previous), f(next)) } - -// def filter[B](p: A0 => B)(implicit cbfMap: CanBuildFrom[Repr[B], B, Repr[B]]) -// : Delta[B, Repr[B]] = { -// val filtered = patch.filter(p) -// if (filtered.isEmpty) Id() -// else {} -// } } - -// -//// def filter[A](f: A => Boolean)(d: Delta[A]): Delta[A] = d match { -//// case Empty => Empty -//// case Update(pel, nel, idx) => -//// if (f(nel)) Update(pel, nel, idx) -//// else Remove(pel, idx) -//// case Insert(el, idx) => -//// if (f(el)) Insert(el, idx) -//// else Remove(el, idx) -//// case Remove(el, idx) => -//// Remove(el, idx) -//// case Combined(d1, d2) => combine(filter(f)(d), filter(f)(d)) -//// } -// -// def map[A, B](d: Delta[A])(f: (A, Int) => (B, Int)): Delta[B] = -// d match { -// case Combined(d1, d2) => combine(map(d1)(f), map(d2)(f)) -// case Update(pel, nel, idx) => -// val (pel0, newIdx) = f(pel, idx) -// val (nel0, _) = f(nel, idx) -// Update(pel0, nel0, newIdx) -// case Insert(a, idx) => -// val (b, newIdx) = f(a, idx) -// Insert(b, newIdx) -// case Remove(a, idx) => -// val (b, newIdx) = f(a, idx) -// Remove(b, newIdx) -// case Empty => Empty -// } -// -// def mapIndex[A](d: Delta[A])(f: Int => Int): Delta[A] = -// map(d) { (el, idx) => -// (el, f(idx)) -// } -// -// def mapElement[A, B](d: Delta[A])(f: A => B): Delta[B] = -// map(d) { (el, idx) => -// (f(el), idx) -// } -// -// def translate[A](d: Delta[A], sum: Int): Delta[A] = -// mapIndex(d)(_ + sum) diff --git a/collection/shared/src/main/scala/hokko/collection/SeqIBehaviorOps.scala b/collection/shared/src/main/scala/hokko/collection/SeqIBehaviorOps.scala index df0821f..0879729 100644 --- a/collection/shared/src/main/scala/hokko/collection/SeqIBehaviorOps.scala +++ b/collection/shared/src/main/scala/hokko/collection/SeqIBehaviorOps.scala @@ -11,17 +11,33 @@ trait SeqIBehaviorOps { implicit class SeqIBehavior[A0, Repr[A0] <: SeqLike[A0, Repr[A0]]]( rep: ICollection[A0, Repr[A0]])(implicit isl: IslAux[Repr[A0], A0]) { - def patch(patches: Event[(Int, GenSeq[A0], Int)])( + private def pendOperation(pend: Event[A0])(f: A0 => Delta[A0, Repr[A0]]) = { + val prependsDelta = pend.map(f) + val newDeltas = Delta.combine(rep.deltas, prependsDelta) + Delta.foldApply(rep.initial, newDeltas) + } + + def :+(append: Event[A0])( + implicit cbf: CanBuildFrom[Repr[A0], A0, Repr[A0]]) + : ICollection[A0, Repr[A0]] = + pendOperation(append)(Append.apply[A0, Repr]) + + def +:(prepends: Event[A0])( + implicit cbf: CanBuildFrom[Repr[A0], A0, Repr[A0]]) + : ICollection[A0, Repr[A0]] = + pendOperation(prepends)(Prepend.apply[A0, Repr]) + + def updated(updates: Event[(Int, A0)])( implicit cbf: CanBuildFrom[Repr[A0], A0, Repr[A0]]) : ICollection[A0, Repr[A0]] = { // map deltas and patches onto left and right val lefties = - rep.deltas.map(Ior.left[Delta[A0, Repr[A0]], (Int, GenSeq[A0], Int)]) + rep.deltas.map(Ior.left[Delta[A0, Repr[A0]], (Int, A0)]) val righties = - patches.map(Ior.right[Delta[A0, Repr[A0]], (Int, GenSeq[A0], Int)]) + updates.map(Ior.right[Delta[A0, Repr[A0]], (Int, A0)]) - // combine deltas and patches into one event (simultaneous occurences + // combine deltas and updates into one event (simultaneous occurences // are merged into Ior.both val combined = lefties.unionWith(righties) { case (Ior.Left(delta), Ior.Right(prepatch)) => @@ -32,9 +48,9 @@ trait SeqIBehaviorOps { // fold combined deltas and patches into an incremental behavior that // tracks both the value as well as any patches that were applied // 1. start with (initial value, None) - val ib: IBehavior[(Repr[A0], Option[Patch[A0, Repr]]), - Ior[Delta[A0, Repr[A0]], (Int, GenSeq[A0], Int)]] = - combined.fold(rep.initial -> Option.empty[Patch[A0, Repr]]) { + val ib: IBehavior[(Repr[A0], Option[Updated[A0, Repr]]), + Ior[Delta[A0, Repr[A0]], (Int, A0)]] = + combined.fold(rep.initial -> Option.empty[Updated[A0, Repr]]) { (acc, ior) => ior match { // 2. delta (left) => (apply to previous value, None) @@ -43,19 +59,15 @@ trait SeqIBehaviorOps { // a) calculate removed elements // b) create patch delta // c) (apply patch, patch) - case Ior.Right((from, newElements, replaced)) => - val oldElements = acc._1.slice(from, from + replaced) - val patch = - Patch[A0, Repr](from, newElements, replaced, oldElements) - patch(acc._1) -> Some(patch) + case Ior.Right((idx, next)) => + val update = Updated[A0, Repr](idx, acc._1(idx), next) + update(acc._1) -> Some(update) // 4. new delta and patch => // repeat 3. // (apply patch apply delta, patch) - case Ior.Both(delta, (from, newElements, replaced)) => - val oldElements = acc._1.slice(from, from + replaced) - val patch = - Patch[A0, Repr](from, newElements, replaced, oldElements) - patch(delta(acc._1)) -> Some(patch) + case Ior.Both(delta, (idx, next)) => + val update = Updated[A0, Repr](idx, acc._1(idx), next) + update(delta(acc._1)) -> Some(update) } } @@ -78,29 +90,5 @@ trait SeqIBehaviorOps { x(acc) } } - - def :+(appends: Event[A0])( - implicit cbf: CanBuildFrom[Repr[A0], A0, Repr[A0]]) - : ICollection[A0, Repr[A0]] = { - val nbAppends = appends.fold(0) { (acc, _) => - acc + 1 - } - val size = rep.toDBehavior.map2(nbAppends.toDBehavior)(_.length + _) - - val patches = size.snapshotWith(appends) { (size, newElement) => - (size, List(newElement), 0) - } - patch(patches) - } - - def +:(prepends: Event[A0])( - implicit cbf: CanBuildFrom[Repr[A0], A0, Repr[A0]]) - : ICollection[A0, Repr[A0]] = - patch(prepends.map(x => (0, List(x), 0))) - - def updated(updates: Event[(Int, A0)])( - implicit cbf: CanBuildFrom[Repr[A0], A0, Repr[A0]]) - : ICollection[A0, Repr[A0]] = - patch(updates.map { case (idx, el) => (idx, List(el), 1) }) } } diff --git a/collection/shared/src/main/scala/hokko/collection/TraversableIBehaviorOps.scala b/collection/shared/src/main/scala/hokko/collection/TraversableIBehaviorOps.scala index ab24540..e135530 100644 --- a/collection/shared/src/main/scala/hokko/collection/TraversableIBehaviorOps.scala +++ b/collection/shared/src/main/scala/hokko/collection/TraversableIBehaviorOps.scala @@ -27,12 +27,6 @@ trait TraversableIBehaviorOps { foldedIBehavior.toDBehavior } -// def filter(p: A0 => Boolean): ICollection[A0, Repr[A0]] = { -// rep.incMap(_ filter p)(Delta.filter(_, p)) { (repr, delta) => -// delta.apply(repr) -// } -// } - def map[B, That](f: A0 => B)( implicit cbf: CanBuildFrom[Repr[A0], B, Repr[B]], cbfDelta: CanBuildFrom[Repr[B], B, Repr[B]],