Skip to content

Commit

Permalink
Implement the record and struct documents
Browse files Browse the repository at this point in the history
  • Loading branch information
Scott Wei committed Sep 4, 2024
1 parent 6ac9e5d commit 532ce24
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 68 deletions.
90 changes: 90 additions & 0 deletions packages/@ot-doc/document/src/lib/algebra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,93 @@ export const ordered = <S>(
equ,
});


export const structLiftUnaryOperator =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
<T extends Record<string, any>>(
getIdn: <K extends keyof T>(key: K) => T[K],
) => (
getEqu: <K extends keyof T>(key: K) => Relation<T[K]>
) => <U extends Partial<T>>(
getUo: <K extends keyof T>(key: K) => UnaryOperator<T[K]>
) => (stt: U): U =>
Object.entries(stt).reduce<U>(
<K extends keyof T>(sttM: U, [key, u]: [K, T[K]]) => {
if (sttM === stt) {
sttM = { ...stt };
}
const newValue = getUo(key)(u);
if (newValue === undefined || getEqu(key)(getIdn(key))(newValue)) {
delete sttM[key];
} else {
sttM[key] = newValue;
}
return sttM;
},
stt
);

export const structLiftPartialBinaryOperator =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
<T extends Record<string, any>>(
getIdn: <K extends keyof T>(key: K) => T[K],
) => (
getEqu: <K extends keyof T>(key: K) => Relation<T[K]>
) => <U extends Partial<T>>(
getPbo: <K extends keyof T>(key: K) => PartialBinaryOperator<T[K]>,
): PartialBinaryOperator<U> =>
(sttA) =>
(sttB) =>
Object.entries(sttB).reduce(
<K extends keyof T>(
stt: U | undefined,
[key, value]: [K, T[K]]
) => {
if (stt === sttA) {
stt = { ...sttA };
}
if (stt) {
const idn = getIdn(key);
const newValue = getPbo(key)(stt[key] ?? idn)(value);
if (newValue === undefined) {
return undefined;
}
if (getEqu(key)(idn)(value)) {
delete stt[key];
} else {
stt[key] = newValue;
}
}
return stt;
},
sttA as U | undefined
);

export const structLiftEqu =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
<T extends Record<string, any>>(
getIdn: <K extends keyof T>(key: K) => T[K],
) => (
getEqu: <K extends keyof T>(key: K) => Relation<T[K]>
): Relation<Partial<T>> => (sttA) => (sttB) => {
if (sttA === sttB) {
return true;
}

for (const key in sttA) {
const valueA = sttA[key] as T[typeof key];
const valueB = sttB[key] as T[typeof key] | undefined;
const equA = getEqu(key)(valueA);
if (!equA(valueB === undefined ? getIdn(key) : valueB)) {
return false
}
}
for (const key in sttB) {
const valueB = sttB[key] as T[typeof key];
const equB = getEqu(key)(valueB);
if (!(key in sttA) && !equB(getIdn(key))) {
return false;
}
}
return true;
}
79 changes: 12 additions & 67 deletions packages/@ot-doc/document/src/lib/record-document.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PartialBinaryOperator, UnaryOperator } from './algebra';
import { structLiftEqu, structLiftPartialBinaryOperator, structLiftUnaryOperator } from './algebra';
import { Document } from './document-core';

/**
Expand Down Expand Up @@ -86,74 +86,19 @@ export const recordDocument = <U>({
comp,
tran,
}: Document<U>): Document<Record<string, U>> => {
const rep: UnaryOperator<Record<string, U>> = (rec) =>
Object.entries(rec).reduce((m, [key, u]) => {
if (equ(idn)(u)) {
if (m === rec) {
// Copy on write
m = { ...rec };
}
delete m[key];
}
return m;
}, rec);
// Lift a UnaryOperator<U> to UnaryOperator<Record<string, U>>
// This is indeed mapping values in objects
const liftUo =
(uo: UnaryOperator<U>): UnaryOperator<Record<string, U>> =>
(rec) =>
Object.entries(rec).reduce((m, [key, u]) => {
m[key] = uo(u);
return m;
}, {} as Record<string, U>);
const getIdn = () => idn;
const getEqu = () => equ;
const getInv = () => inv;
const getComp = () => comp;
const getTran = () => tran;
const liftUo = structLiftUnaryOperator(getIdn)(getEqu);
const liftPbo = structLiftPartialBinaryOperator(getIdn)(getEqu);

// Lift a PartialBinaryOperator<U> to PartialBinaryOperator<Record<string, U>>
// It assumes the pbo (denoted by (.)) has the right identity property
// ∀ a ∈ U, a . ι = a
// Both (*) and (/) has this property (@4.1, @4.2)
const liftPbo =
(pbo: PartialBinaryOperator<U>): PartialBinaryOperator<Record<string, U>> =>
(recA) =>
(recB) =>
Object.entries(recB).reduce(
(m, [key, u]) => {
if (m) {
const value = pbo(m[key] ?? idn)(u);
if (value === undefined) {
return undefined;
}
if (equ(idn)(value)) {
delete m[key];
} else {
m[key] = value;
}
}
return m;
},
{ ...recA } as Record<string, U> | undefined
);
return {
idn: {},
inv: liftUo(inv),
comp: liftPbo(comp),
tran: liftPbo(tran),
equ: (a) => (b) => {
if (a === b) {
return true;
}
const repA = rep(a);
const repB = rep(b);
if (Object.keys(repA).length !== Object.keys(repB).length) {
return false;
}
for (const key in repA) {
const uA = repA[key] ?? null;
const uB = repB[key] ?? null;
if (uA === null || uB === null || !equ(uA)(uB)) {
return false;
}
}
return true;
},
inv: liftUo(getInv),
comp: liftPbo(getComp),
tran: liftPbo(getTran),
equ: structLiftEqu(getIdn)(getEqu),
};
};
18 changes: 17 additions & 1 deletion packages/@ot-doc/document/src/lib/struct-document.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { structLiftEqu, structLiftPartialBinaryOperator, structLiftUnaryOperator } from "./algebra";
import { Document } from "./document-core";

/**
Expand Down Expand Up @@ -74,6 +75,21 @@ type DocumentStruct<T extends Record<string, unknown>> = {
[K in keyof T]: Document<T[K]>;
};

export const structDocument = <T extends Record<string, unknown>>(struct: DocumentStruct<T>): Document<T> => {
export const structDocument = <T extends Record<string, unknown>>(struct: DocumentStruct<T>): Document<Partial<T>> => {
const getIdn = <K extends keyof T>(key: K) => struct[key].idn;
const getEqu = <K extends keyof T>(key: K) => struct[key].equ;
const getInv = <K extends keyof T>(key: K) => struct[key].inv;
const getComp = <K extends keyof T>(key: K) => struct[key].comp;
const getTran = <K extends keyof T>(key: K) => struct[key].tran;
const liftUo = structLiftUnaryOperator(getIdn)(getEqu);
const liftPbo = structLiftPartialBinaryOperator(getIdn)(getEqu);

return {
idn: {},
inv: liftUo(getInv),
comp: liftPbo(getComp),
tran: liftPbo(getTran),
equ: structLiftEqu(getIdn)(getEqu),
};

};

0 comments on commit 532ce24

Please sign in to comment.