import * as E from "@principia/prelude/Eq";
import { memoize } from "../_utils";
import * as A from "../Array";
import type { ReadonlyRecord } from "../Record";
import * as R from "../Record";
import type { Eq } from "./model";
/*
* -------------------------------------------
* Eq Combinators
* -------------------------------------------
*/
export * from "@principia/prelude/Eq/combinators";
export const nullable = (or: Eq): Eq =>
E.fromEquals((x, y) => (x === null || y === null ? x === y : or.equals_(x, y)));
export const type: (eqs: { [K in keyof A]: Eq }) => Eq<{ [K in keyof A]: A[K] }> = E.getStructEq;
export const partial = (properties: { [K in keyof A]: Eq }): Eq> =>
E.fromEquals((x, y) => {
for (const k in properties) {
const xk = x[k];
const yk = y[k];
if (!(xk === undefined || yk === undefined ? xk === yk : properties[k].equals_(xk!, yk!))) {
return false;
}
}
return true;
});
export const record: (codomain: Eq) => Eq> = R.getEq;
export const array: (item: Eq) => Eq> = A.getEq;
export const tuple: >(
...components: { [K in keyof A]: Eq }
) => Eq = E.getTupleEq as any;
export const intersect_ = (left: Eq, right: Eq): Eq =>
E.fromEquals((x, y) => left.equals_(x, y) && right.equals_(x, y));
export const intersect = (right: Eq) => (left: Eq): Eq => intersect_(left, right);
export const sum_ = (
tag: T,
members: { [K in keyof A]: Eq> }
): Eq =>
E.fromEquals((x: ReadonlyRecord, y: ReadonlyRecord) => {
const vx = x[tag];
const vy = y[tag];
if (vx !== vy) {
return false;
}
return members[vx].equals(x, y);
});
export const sum = (tag: T) => (
members: { [K in keyof A]: Eq> }
): Eq => sum_(tag, members);
export const lazy = (f: () => Eq): Eq => {
const get = memoize>(f);
return E.fromEquals((x, y) => get().equals_(x, y));
};