Skip to content

Commit

Permalink
Add a cp+op version documents
Browse files Browse the repository at this point in the history
  • Loading branch information
wewei committed Sep 18, 2024
1 parent 23b32a1 commit 48a5a41
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 0 deletions.
18 changes: 18 additions & 0 deletions packages/@ot-doc/document/src/lib2/algebra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Maybe } from "./maybe";

export type Constant<T> = () => T;
export type UnaryOperator<T> = (a: T) => T;
export type PartialUnaryOperator<T> = (a: T) => Maybe<T>;
export type BinaryOperator<T> = (a: T) => UnaryOperator<T>;
export type PartialBinaryOperator<T> = (a: T) => PartialUnaryOperator<T>;
export type Predicate<T> = (a: T) => boolean;
export type Relation<T> = (a: T) => Predicate<T>;

// Use $PascalCase to represent a type class
export type $Eq<T> = {
equals: Relation<T>;
};

export type $Ord<T> = {
lessThan: Relation<T>;
};
31 changes: 31 additions & 0 deletions packages/@ot-doc/document/src/lib2/document.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Constant, PartialBinaryOperator, PartialUnaryOperator, UnaryOperator } from "./algebra";

export type $Init<Cp> = {
initial: Constant<Cp>;
};

export const $init = <Cp>(cp: Cp): $Init<Cp> => ({ initial: () => cp });

export type $Comp<Cp, Op> = {
compose: (op: Op) => PartialUnaryOperator<Cp>;
};

export type $Inv<Op> = {
invert: UnaryOperator<Op>;
};

export type $Idn<Op> = {
identity: Constant<Op>;
};

export const $idn = <Op>(op: Op): $Idn<Op> => ({ identity: () => op });

export type $Tran<Op> = {
transform: PartialBinaryOperator<Op>;
};

export type $BaseDoc<Cp, Op> = $Init<Cp> & $Comp<Cp, Op>;

export type $InvDoc<Cp, Op> = $BaseDoc<Cp, Op> & $Inv<Op>;

export type $FullDoc<Cp, Op> = $InvDoc<Cp, Op> & $Idn<Op> & $Tran<Op>;
6 changes: 6 additions & 0 deletions packages/@ot-doc/document/src/lib2/maybe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// $: Constructor type
// v: the value
export type Maybe<T> = { $: "Nothing" } | { $: "Just", v: T };

export const nothing = <T>(): Maybe<T> => ({ $: "Nothing" });
export const just = <T>(v: T): Maybe<T> => ({ $: "Just", v });
87 changes: 87 additions & 0 deletions packages/@ot-doc/document/src/lib2/record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { $Eq } from "./algebra";
import { $BaseDoc, $FullDoc, $Idn, $InvDoc } from "./document";
import { just, nothing } from "./maybe";

export const $baseDocRecord =
<Cp>({ equals }: $Eq<Cp>) =>
<Op>({
initial,
compose,
}: $BaseDoc<Cp, Op>): $BaseDoc<Record<string, Cp>, Record<string, Op>> => {
const cpI = initial();

return {
initial: () => ({}),
compose: (op) => (cp) =>
Object.keys(op).reduce((cpT, key) => {
if (cpT.$ === 'Nothing') {
return cpT;
}
const cpK = cpT.v[key] ?? cpI;
const opK = op[key];
const mNewCpK = compose(opK)(cpK);
if (mNewCpK.$ === 'Nothing') {
return nothing();
}
const { v: newCpK } = mNewCpK;
if (!equals(newCpK)(cpK)) {
if (cpT.v === cp) {
cpT.v = { ...cp };
}
if (equals(newCpK)(cpI)) {
delete cpT.v[key];
} else {
cpT.v[key] = newCpK;
}
}
return cpT;
}, just(cp)),
};
};

export const $invDocRecord =
<Cp>({ equals }: $Eq<Cp>) =>
<Op>({ initial, compose, invert }: $InvDoc<Cp, Op>): $InvDoc<Record<string, Cp>, Record<string, Op>> => {
return {
...$baseDocRecord({ equals })({ initial, compose }),
invert: (op) => Object.keys(op).reduce((opT, key) => {
opT[key] = invert(op[key]);
return opT;
}, {} as Record<string, Op>),
};
};

export const $fullDocRecord =
<Op>(clsOp: $Eq<Op> & $Idn<Op>) =>
<Cp>(clsCp: $Eq<Cp>) =>
({ initial, compose, invert, transform }: $FullDoc<Cp, Op>): $FullDoc<Record<string, Cp>, Record<string, Op>> => {
const opI = clsOp.identity();

return {
...$invDocRecord(clsCp)({ initial, compose, invert }),
identity: () => ({}),
transform: (opA) => (opB) => Object.keys(opA).reduce((mOpT, key) => {
if (mOpT.$ === 'Nothing' || !(key in opB)) {
return mOpT;
}
const opKA = mOpT.v[key];
const opKB = opB[key];
const mNewOpKA = transform(opKA)(opKB);
if (mNewOpKA.$ === 'Nothing') {
return nothing();
}
const { v: newOpKA } = mNewOpKA;
if (!clsOp.equals(newOpKA)(opKA)) {
if (mOpT.v === opA) {
mOpT.v = { ...opA };
}
if (clsOp.equals(newOpKA)(opI)) {
delete mOpT.v[key];
} else {
mOpT.v[key] = newOpKA;
}
}
return mOpT;
}, just(opA)),
};
};
51 changes: 51 additions & 0 deletions packages/@ot-doc/document/src/lib2/singleton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { $Eq, $Ord } from "./algebra";
import { $FullDoc, $idn, $Init, $InvDoc } from "./document";
import { just, nothing } from "./maybe";

// f: update from value
// t: update to value
export type Update<A> = { f: A, t: A } | null;

// Eq a => Eq (Update a)
export const $eqUpdate = <A>({ equals }: $Eq<A>): $Eq<Update<A>> => ({
equals: (a) => (b) => {
if (a === b) {
return true;
}
if (!a || !b) {
return false;
}
return (equals(a.f)(b.f) && equals(a.t)(b.t))
},
});

// Eq a, Initial a => $InvDoc a (Update a)
export const $invDocUpdate = <A>({
equals,
initial,
}: $Eq<A> & $Init<A>): $InvDoc<A, Update<A>> => ({
initial,
compose:
(op) => op ? ((v) => equals(v)(op.f) ? just(op.t) : nothing()) : just,
invert: op => op ? { f: op.t, t: op.f } : null,
});

// Greater Write Win
// Eq a, Initial a, Ord a => $FullDoc a (Update a)
export const $fullDocGww = <A>({
equals,
initial,
lessThan,
}: $Eq<A> & $Init<A> & $Ord<A>): $FullDoc<A, Update<A>> => ({
...$invDocUpdate({ equals, initial }),
...$idn(null),
transform: (opA) => (opB) => {
if (!opA) return just(null);
if (!opB) return just(opA);
const { f: fA, t: tA } = opA;
const { f: fB, t: tB } = opB;
if (!equals(fA)(fB)) return nothing();
return just(lessThan(tA)(tB) ? { f: tA, t: tB } : null);
},
});

37 changes: 37 additions & 0 deletions packages/@ot-doc/document/src/lib2/struct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { $Eq } from "./algebra";
import { $BaseDoc, $FullDoc, $InvDoc } from "./document";

export type Stt$Eq<T extends Record<string, unknown>> = {
[K in keyof T]: $Eq<T[K]>;
};

export type Stt$BaseDoc<Cp extends Record<string, unknown>, Op extends Record<keyof Cp, unknown>> = {
[K in keyof Cp]: $BaseDoc<Cp[K], Op[K]>;
};

export type Stt$InvDoc<Cp extends Record<string, unknown>, Op extends Record<keyof Cp, unknown>> = {
[K in keyof Cp]: $InvDoc<Cp[K], Op[K]>;
};

export type Stt$FullDoc<Cp extends Record<string, unknown>, Op extends Record<keyof Cp, unknown>> = {
[K in keyof Cp]: $FullDoc<Cp[K], Op[K]>;
};

export const $baseDocStruct =
<Cp extends Record<string, unknown>>(stt$eq: Stt$Eq<Cp>) =>
<Op extends Record<keyof Cp, unknown>>(stt$baseDoc: Stt$BaseDoc<Cp, Op>): $BaseDoc<Cp, Op> => {
return {};
};

export const $invDocStruct =
<Cp extends Record<string, unknown>>(stt$eq: Stt$Eq<Cp>) =>
<Op extends Record<keyof Cp, unknown>>(stt$invDoc: Stt$InvDoc<Cp, Op>): $InvDoc<Cp, Op> => {
return {};
};

export const $fullDocStruct =
<Cp extends Record<string, unknown>>(stt$eq: Stt$Eq<Cp>) =>
<Op extends Record<keyof Cp, unknown>>(stt$fullDoc: Stt$FullDoc<Cp, Op>): $FullDoc<Cp, Op> => {
return {};
};

25 changes: 25 additions & 0 deletions packages/@ot-doc/document/src/lib2/timestamped.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { $Eq, $Ord } from "./algebra";
import { $FullDoc, $Init } from "./document";
import { $fullDocGww, Update } from "./singleton";

// t: timestamp
// v: value
export type Timestamped<T> = { t: number, v: T };

export const $eqTimestamped = <T>({ equals }: $Eq<T>): $Eq<Timestamped<T>> => ({
equals: (a) => (b) => a.t === b.t && equals(a.v)(b.v),
});

export const $ordTimestamped = <T>({ lessThan }: $Ord<T>): $Ord<Timestamped<T>> => ({
lessThan: (a) => (b) => a.t < b.t || (a.t === b.t && lessThan(a.v)(b.v)),
});

export const $fullDocLww = <A>(
cls: $Eq<A> & $Init<A> & $Ord<A>
): $FullDoc<Timestamped<A>, Update<Timestamped<A>>> => {
const v = cls.initial();
const initial = () => ({ t: -Infinity, v });
const { equals } = $eqTimestamped(cls);
const { lessThan } = $ordTimestamped(cls);
return $fullDocGww({ equals, lessThan, initial });
};

0 comments on commit 48a5a41

Please sign in to comment.