restructure code a bit, add comparison functions for primitive types and composed types (needed to put values in sets)
This commit is contained in:
parent
3978f7f835
commit
8653bb99c6
12 changed files with 175 additions and 64 deletions
29
compare/primitives.js
Normal file
29
compare/primitives.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Char, Double, Int } from "../primitives/types.js";
|
||||||
|
|
||||||
|
export const compareNumbers = x => y => {
|
||||||
|
if (typeof(x) !== 'number' || typeof(y) !== 'number') {
|
||||||
|
throw new Error(`was only meant to compare numbers! got ${x} and ${y}`);
|
||||||
|
}
|
||||||
|
return (x < y) ? -1 : (x > y) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const compareStrings = x => y => {
|
||||||
|
if (typeof(x) !== 'string' || typeof(y) !== 'string') {
|
||||||
|
throw new Error(`was only meant to compare strings! got ${x} and ${y}`);
|
||||||
|
}
|
||||||
|
return (x < y) ? -1 : (x > y) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const compareBools = x => y => {
|
||||||
|
if (typeof(x) !== 'boolean' || typeof(y) !== 'boolean') {
|
||||||
|
throw new Error(`was only meant to compare booleans! got ${x} and ${y}`);
|
||||||
|
}
|
||||||
|
return x - y;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typeToCmp = new Map([
|
||||||
|
[Int, compareNumbers],
|
||||||
|
[Char, compareStrings],
|
||||||
|
[Double, compareNumbers],
|
||||||
|
[Boolean, compareBools],
|
||||||
|
]);
|
||||||
58
compare/structures.js
Normal file
58
compare/structures.js
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { compareNumbers } from "./primitives.js"
|
||||||
|
import { length as lengthLs } from "../structures/list.js";
|
||||||
|
import { read, length as lengthSet } from "../structures/set.js";
|
||||||
|
import { constructorProduct, getLeft, getRight } from "../structures/product.js";
|
||||||
|
import { match } from "../structures/sum.js";
|
||||||
|
|
||||||
|
// (a -> a -> Int) -> [a] -> [a] -> Int
|
||||||
|
export const compareLists = compareElems => x => y => {
|
||||||
|
return compareNumbers(lengthLs(x))(lengthLs(y))
|
||||||
|
|| (() => {
|
||||||
|
for (let i=0; i<lengthLs(x); i++) {
|
||||||
|
const elemCmp = compareElems(get(x)(i))(get(y)(i));
|
||||||
|
if (elemCmp !== 0) {
|
||||||
|
return elemCmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
|
||||||
|
// (a -> a -> Int) -> (b -> b -> Int) -> (a, b) -> (a, b) -> Int
|
||||||
|
export const compareProducts = compareLeft => compareRight => x => y => {
|
||||||
|
return compareLeft (getLeft (x))(getLeft (y))
|
||||||
|
|| compareRight(getRight(x))(getRight(y));
|
||||||
|
};
|
||||||
|
|
||||||
|
// (a -> a -> Int) -> (b -> b -> Int) -> (a | b) -> (a | b) -> Int
|
||||||
|
export const compareSums = compareLeft => compareRight => x => y => {
|
||||||
|
return match(x)(constructorProduct
|
||||||
|
(leftValueX => match(y)(constructorProduct
|
||||||
|
(leftValueY => compareLeft(leftValueX)(leftValueY))
|
||||||
|
((rightValueY) => -1) // x is 'left' and y is 'right' => x < y
|
||||||
|
))
|
||||||
|
(rightValueX => match(y)(constructorProduct
|
||||||
|
(leftValueY => 1) // x is 'right' and y is 'left' => x > y
|
||||||
|
(rightValueY => compareRight(rightValueX)(rightValueY))
|
||||||
|
))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// (a -> a -> Int) -> {a} -> {a} -> Int
|
||||||
|
export const compareSets = compareElems => x => y => {
|
||||||
|
return compareNumbers(lengthSet(x))(lengthSet(y))
|
||||||
|
|| (() => {
|
||||||
|
// sets have same size -> iterate over both sets and compare their elements pairwise
|
||||||
|
// because of the underlying red-black tree, iteration happens in ordered fashion
|
||||||
|
const iterate = iterX => iterY =>
|
||||||
|
read(iterX)
|
||||||
|
(keyX => nextX =>
|
||||||
|
read(iterY)
|
||||||
|
// we could also use the comparison function that is embedded in the set object,
|
||||||
|
// but to be consistent with the other comparison-functions, we don't.
|
||||||
|
(keyY => nextY => compareElems(keyX)(keyY) || iterate(nextX)(nextY))
|
||||||
|
(0)) // end of set y (we'll never get here because sets are same size)
|
||||||
|
(0) // end of set x;
|
||||||
|
iterate(first(x))(first(y));
|
||||||
|
})();
|
||||||
|
};
|
||||||
31
compare/versioning.js
Normal file
31
compare/versioning.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
export const compareSlots = compareElems => slotA => slotB => {
|
||||||
|
if (slotA.depth < slotB.depth) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (slotB.depth < slotA.depth) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return compareValues(compareElems)(slotA.value)(slotB.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const compareValues = compareElems => valA => valB => {
|
||||||
|
if (valA.kind < valB.kind) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (valB.kind < valA.kind) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (valA.kind === "literal") {
|
||||||
|
return compareElems(valA.out)(valB.out);
|
||||||
|
}
|
||||||
|
if (valA.kind === "read") {
|
||||||
|
return compareSlots(compareElems)(valA.slot)(valB.slot);
|
||||||
|
}
|
||||||
|
if (valA.kind === "transformation") {
|
||||||
|
const cmpIn = compareValues(compareElems)(valA.in)(valB.in);
|
||||||
|
if (cmpIn !== 0) {
|
||||||
|
return cmpIn;
|
||||||
|
}
|
||||||
|
return compareValues(compareElems)(valA.fn)(valB.fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -43,5 +43,3 @@ export const ModuleSymbols = {l:[
|
||||||
{i: SymbolType , t: SymbolT},
|
{i: SymbolType , t: SymbolT},
|
||||||
{i: SymbolGenericType, t: SymbolT},
|
{i: SymbolGenericType, t: SymbolT},
|
||||||
]};
|
]};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { newLiteral, transform, read, getReadDependencies, verifyValue } from ".
|
||||||
import { merge, merge2, newSlot, overwrite } from "../versioning/slot.js";
|
import { merge, merge2, newSlot, overwrite } from "../versioning/slot.js";
|
||||||
import createRBTree from "functional-red-black-tree";
|
import createRBTree from "functional-red-black-tree";
|
||||||
import { add, emptySet, RBTreeWrapper } from "../structures/set.js";
|
import { add, emptySet, RBTreeWrapper } from "../structures/set.js";
|
||||||
|
import { compareNumbers } from "../compare/primitives.js";
|
||||||
|
|
||||||
const inc = x => x + 1;
|
const inc = x => x + 1;
|
||||||
|
|
||||||
|
|
@ -58,14 +59,8 @@ const sixSlot = overwrite(fiveSlot)(newLiteral(6));
|
||||||
const sevenSlot = overwrite(fiveSlot)(newLiteral(7));
|
const sevenSlot = overwrite(fiveSlot)(newLiteral(7));
|
||||||
const eightSlot = overwrite(fiveSlot)(newLiteral(8));
|
const eightSlot = overwrite(fiveSlot)(newLiteral(8));
|
||||||
|
|
||||||
const numCompare = x => y => {
|
const intMerge = merge(compareNumbers);
|
||||||
if (typeof(x) !== 'number' || typeof(y) !== 'number') {
|
const intMerge2 = merge2(compareNumbers);
|
||||||
throw new Error(`was only meant to compare numbers! got ${x} and ${y}`);
|
|
||||||
}
|
|
||||||
return (x < y) ? -1 : (x > y) ? 1 : 0;
|
|
||||||
};
|
|
||||||
const intMerge = merge(numCompare);
|
|
||||||
const intMerge2 = merge2(numCompare);
|
|
||||||
|
|
||||||
const sixSevenSlot = intMerge(sixSlot)(sevenSlot);
|
const sixSevenSlot = intMerge(sixSlot)(sevenSlot);
|
||||||
const sevenEightSlot = intMerge(sevenSlot)(eightSlot);
|
const sevenEightSlot = intMerge(sevenSlot)(eightSlot);
|
||||||
|
|
@ -83,13 +78,6 @@ console.log(pretty({sixSevenEightSlot}));
|
||||||
// console.log("## Heterogeneous data ##");
|
// console.log("## Heterogeneous data ##");
|
||||||
// console.log("########################");
|
// console.log("########################");
|
||||||
|
|
||||||
// const strCompare = x => y => {
|
|
||||||
// if (typeof(x) !== 'string' || typeof(y) !== 'string') {
|
|
||||||
// throw new Error(`was only meant to compare strings! got ${x} and ${y}`);
|
|
||||||
// }
|
|
||||||
// return (x < y) ? -1 : (x > y) ? 1 : 0;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Slot<Int>
|
// // Slot<Int>
|
||||||
// const numberOfSheepSlot = newSlot(Symbol('numberOfSheep'))(newLiteral(5));
|
// const numberOfSheepSlot = newSlot(Symbol('numberOfSheep'))(newLiteral(5));
|
||||||
// const alternativeNumberOfSheepSlot = newSlot(Symbol('alternativeNumberOfSheep'))(newLiteral(6));
|
// const alternativeNumberOfSheepSlot = newSlot(Symbol('alternativeNumberOfSheep'))(newLiteral(6));
|
||||||
|
|
@ -108,7 +96,7 @@ console.log(pretty({sixSevenEightSlot}));
|
||||||
// transform(read(labelSlot))(combineFn)));
|
// transform(read(labelSlot))(combineFn)));
|
||||||
|
|
||||||
// console.log(
|
// console.log(
|
||||||
// add(add(emptySet(compareSlots(strCompare)))(labelAndValueSlotA))(labelAndValueSlotB)
|
// add(add(emptySet(compareSlots(compareStrings)))(labelAndValueSlotA))(labelAndValueSlotB)
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// merge()(labelSlot)(labelAndValueSlot)
|
// merge()(labelSlot)(labelAndValueSlot)
|
||||||
|
|
@ -119,4 +107,9 @@ console.log("## RB Tree ##")
|
||||||
console.log("#############")
|
console.log("#############")
|
||||||
|
|
||||||
// just a small experiment
|
// just a small experiment
|
||||||
console.log(new RBTreeWrapper(createRBTree().insert(1).insert(1).insert(2)));
|
console.log(
|
||||||
|
createRBTree()
|
||||||
|
.insert(1)
|
||||||
|
.insert(1)
|
||||||
|
.insert(2)
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,20 @@ import { lsType } from "./types.js";
|
||||||
import { Typed } from "../typed.js"
|
import { Typed } from "../typed.js"
|
||||||
|
|
||||||
// 'normal' implementation
|
// 'normal' implementation
|
||||||
const emptyList = {l:[]};
|
export const emptyList = {l:[]};
|
||||||
const emptyListType = makeGeneric(a => lsType(a));
|
const emptyListType = makeGeneric(a => lsType(a));
|
||||||
const get = ls => i => ls.l[i];
|
export const get = ls => i => ls.l[i];
|
||||||
const put = ls => i => elem => ({l: ls.l.with(Number(i), elem)});
|
export const put = ls => i => elem => ({l: ls.l.with(Number(i), elem)});
|
||||||
const push = ls => elem => ({l:ls.l.concat([elem])});
|
export const push = ls => elem => ({l:ls.l.concat([elem])});
|
||||||
const map = ls => fn => ({ l: ls.l.map(elem => fn(elem)) });
|
export const map = ls => fn => ({ l: ls.l.map(elem => fn(elem)) });
|
||||||
|
export const length = ls => ls.l.length;
|
||||||
|
export const fold = ls => callback => initial => {
|
||||||
|
let acc = initial;
|
||||||
|
for (let i=0; i<ls.l.length; i++) {
|
||||||
|
acc = callback(acc)(ls.l[i]);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
export const String = lsType(Char); // alias
|
export const String = lsType(Char); // alias
|
||||||
export const Module = lsType(Typed);
|
export const Module = lsType(Typed);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
// Product-type (also called: pair, tuple)
|
||||||
|
// A Product-type always has only two fields, called "left" and "right".
|
||||||
|
// Product-types of more fields (called Structs) can be constructed by nesting Product-types.
|
||||||
|
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
import { makeGeneric } from "../generics/generics.js";
|
||||||
import { GenericType, Type } from "../primitives/types.js";
|
import { GenericType, Type } from "../primitives/types.js";
|
||||||
import { typedFnType } from "./types.js";
|
import { typedFnType } from "./types.js";
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,20 @@ export const emptySet = compareFn => new RBTreeWrapper(createRBTree((x, y) => co
|
||||||
export const has = set => key => set.tree.get(key) === true;
|
export const has = set => key => set.tree.get(key) === true;
|
||||||
export const add = set => key => set.tree.get(key) === true ? set : new RBTreeWrapper(set.tree.insert(key, true));
|
export const add = set => key => set.tree.get(key) === true ? set : new RBTreeWrapper(set.tree.insert(key, true));
|
||||||
export const remove = set => key => new RBTreeWrapper(set.tree.remove(key));
|
export const remove = set => key => new RBTreeWrapper(set.tree.remove(key));
|
||||||
|
export const length = set => set.tree.length;
|
||||||
|
|
||||||
|
export const first = set => set.tree.begin;
|
||||||
|
export const last = set => set.tree.end;
|
||||||
|
|
||||||
|
// test if iterator is 'done', and if not, get element and advance iterator.
|
||||||
|
export const read = iter => ifNotDone => ifDone => {
|
||||||
|
if (iter !== undefined && iter.valid) {
|
||||||
|
return ifNotDone(iter.key)(iter.clone().next());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ifDone();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const forEach = set => fn => {
|
export const forEach = set => fn => {
|
||||||
set.tree.forEach(key => { fn(key); });
|
set.tree.forEach(key => { fn(key); });
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@ function capitalizeFirstLetter(val) {
|
||||||
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// [{l: "x", r: Double}, {l: "y", r: Double}] => (Double × (Double × Unit))
|
// 'fields' is an array of (name: string, type: Type) pairs.
|
||||||
|
// e.g.:
|
||||||
|
// [{l: "x", r: Double}, {l: "y", r: Double}]
|
||||||
|
// results in the type (Double × (Double × Unit))
|
||||||
export const structType = fields => {
|
export const structType = fields => {
|
||||||
if (fields.length === 0) {
|
if (fields.length === 0) {
|
||||||
return Unit;
|
return Unit;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,22 @@
|
||||||
|
// Sum-type (also called: tagged union, disjoin union, variant type)
|
||||||
|
// A Sum-type always has only two variants, called "left" and "right".
|
||||||
|
// Sum-types of more variants (called Enums) can be constructed by nesting Sum-types.
|
||||||
|
|
||||||
import { prodType } from "./types.js";
|
import { prodType } from "./types.js";
|
||||||
import { GenericType, Type } from "../primitives/types.js";
|
import { GenericType, Type } from "../primitives/types.js";
|
||||||
import { typedFnType } from "./types.js";
|
import { typedFnType } from "./types.js";
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
import { makeGeneric } from "../generics/generics.js";
|
||||||
import { sumType } from "./types.js";
|
import { sumType } from "./types.js";
|
||||||
|
|
||||||
export const constructorLeft = left => ({variant: "L", value: left });
|
export const constructorLeft = left => ({t: "L", v: left }); // 't': tag, 'v': value
|
||||||
export const constructorRight = right => ({variant: "R", value: right});
|
export const constructorRight = right => ({t: "R", v: right});
|
||||||
|
|
||||||
// signature:
|
// signature:
|
||||||
// sum-type -> (leftType -> resultType, rightType -> resultType) -> resultType
|
// sum-type -> (leftType -> resultType, rightType -> resultType) -> resultType
|
||||||
export const match = sum => handlers => sum.variant === "L"
|
export const match = sum => handlers =>
|
||||||
? handlers.left(sum.value)
|
sum.t === "L"
|
||||||
: handlers.right(sum.value);
|
? handlers.left(sum.v)
|
||||||
|
: handlers.right(sum.v);
|
||||||
|
|
||||||
export const ModuleSum = {l:[
|
export const ModuleSum = {l:[
|
||||||
// binary type constructor
|
// binary type constructor
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { add, emptySet, forEach } from "../structures/set.js";
|
import { add, emptySet, forEach } from "../structures/set.js";
|
||||||
import { deepEqual } from "../util/util.js";
|
import { deepEqual } from "../util/util.js";
|
||||||
import {inspect} from "node:util";
|
import {inspect} from "node:util";
|
||||||
|
import { compareSlots } from "../compare/versioning.js";
|
||||||
|
|
||||||
// UUID -> Value<a> -> Slot<a>
|
// UUID -> Value<a> -> Slot<a>
|
||||||
export const newSlot = uuid => value => ({
|
export const newSlot = uuid => value => ({
|
||||||
|
|
@ -10,39 +11,6 @@ export const newSlot = uuid => value => ({
|
||||||
[inspect.custom]: (depth, options, inspect) => `newSlot(${inspect(uuid)}, ${inspect(value)})`,
|
[inspect.custom]: (depth, options, inspect) => `newSlot(${inspect(uuid)}, ${inspect(value)})`,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const compareSlots = compareElems => slotA => slotB => {
|
|
||||||
if (slotA.depth < slotB.depth) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (slotB.depth < slotA.depth) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return compareValues(compareElems)(slotA.value)(slotB.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const compareValues = compareElems => valA => valB => {
|
|
||||||
if (valA.kind < valB.kind) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (valB.kind < valA.kind) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (valA.kind === "literal") {
|
|
||||||
return compareElems(valA.out)(valB.out);
|
|
||||||
}
|
|
||||||
if (valA.kind === "read") {
|
|
||||||
return compareSlots(compareElems)(valA.slot)(valB.slot);
|
|
||||||
}
|
|
||||||
if (valA.kind === "transformation") {
|
|
||||||
const cmpIn = compareValues(compareElems)(valA.in)(valB.in);
|
|
||||||
if (cmpIn !== 0) {
|
|
||||||
return cmpIn;
|
|
||||||
}
|
|
||||||
return compareValues(compareElems)(valA.fn)(valB.fn);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Slot<a> -> Value<a> -> Slot<a>
|
// Slot<a> -> Value<a> -> Slot<a>
|
||||||
export const overwrite = slot => value => ({
|
export const overwrite = slot => value => ({
|
||||||
overwrites: slot,
|
overwrites: slot,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue