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: SymbolGenericType, t: SymbolT},
|
||||
]};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { newLiteral, transform, read, getReadDependencies, verifyValue } from ".
|
|||
import { merge, merge2, newSlot, overwrite } from "../versioning/slot.js";
|
||||
import createRBTree from "functional-red-black-tree";
|
||||
import { add, emptySet, RBTreeWrapper } from "../structures/set.js";
|
||||
import { compareNumbers } from "../compare/primitives.js";
|
||||
|
||||
const inc = x => x + 1;
|
||||
|
||||
|
|
@ -58,14 +59,8 @@ const sixSlot = overwrite(fiveSlot)(newLiteral(6));
|
|||
const sevenSlot = overwrite(fiveSlot)(newLiteral(7));
|
||||
const eightSlot = overwrite(fiveSlot)(newLiteral(8));
|
||||
|
||||
const numCompare = 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;
|
||||
};
|
||||
const intMerge = merge(numCompare);
|
||||
const intMerge2 = merge2(numCompare);
|
||||
const intMerge = merge(compareNumbers);
|
||||
const intMerge2 = merge2(compareNumbers);
|
||||
|
||||
const sixSevenSlot = intMerge(sixSlot)(sevenSlot);
|
||||
const sevenEightSlot = intMerge(sevenSlot)(eightSlot);
|
||||
|
|
@ -83,13 +78,6 @@ console.log(pretty({sixSevenEightSlot}));
|
|||
// console.log("## Heterogeneous data ##");
|
||||
// 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>
|
||||
// const numberOfSheepSlot = newSlot(Symbol('numberOfSheep'))(newLiteral(5));
|
||||
// const alternativeNumberOfSheepSlot = newSlot(Symbol('alternativeNumberOfSheep'))(newLiteral(6));
|
||||
|
|
@ -108,7 +96,7 @@ console.log(pretty({sixSevenEightSlot}));
|
|||
// transform(read(labelSlot))(combineFn)));
|
||||
|
||||
// console.log(
|
||||
// add(add(emptySet(compareSlots(strCompare)))(labelAndValueSlotA))(labelAndValueSlotB)
|
||||
// add(add(emptySet(compareSlots(compareStrings)))(labelAndValueSlotA))(labelAndValueSlotB)
|
||||
// );
|
||||
|
||||
// merge()(labelSlot)(labelAndValueSlot)
|
||||
|
|
@ -119,4 +107,9 @@ console.log("## RB Tree ##")
|
|||
console.log("#############")
|
||||
|
||||
// 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"
|
||||
|
||||
// 'normal' implementation
|
||||
const emptyList = {l:[]};
|
||||
export const emptyList = {l:[]};
|
||||
const emptyListType = makeGeneric(a => lsType(a));
|
||||
const get = ls => i => ls.l[i];
|
||||
const put = ls => i => elem => ({l: ls.l.with(Number(i), elem)});
|
||||
const push = ls => elem => ({l:ls.l.concat([elem])});
|
||||
const map = ls => fn => ({ l: ls.l.map(elem => fn(elem)) });
|
||||
export const get = ls => i => ls.l[i];
|
||||
export const put = ls => i => elem => ({l: ls.l.with(Number(i), elem)});
|
||||
export const push = ls => elem => ({l:ls.l.concat([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 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 { GenericType, Type } from "../primitives/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 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 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 => {
|
||||
set.tree.forEach(key => { fn(key); });
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ function capitalizeFirstLetter(val) {
|
|||
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 => {
|
||||
if (fields.length === 0) {
|
||||
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 { GenericType, Type } from "../primitives/types.js";
|
||||
import { typedFnType } from "./types.js";
|
||||
import { makeGeneric } from "../generics/generics.js";
|
||||
import { sumType } from "./types.js";
|
||||
|
||||
export const constructorLeft = left => ({variant: "L", value: left });
|
||||
export const constructorRight = right => ({variant: "R", value: right});
|
||||
export const constructorLeft = left => ({t: "L", v: left }); // 't': tag, 'v': value
|
||||
export const constructorRight = right => ({t: "R", v: right});
|
||||
|
||||
// signature:
|
||||
// sum-type -> (leftType -> resultType, rightType -> resultType) -> resultType
|
||||
export const match = sum => handlers => sum.variant === "L"
|
||||
? handlers.left(sum.value)
|
||||
: handlers.right(sum.value);
|
||||
export const match = sum => handlers =>
|
||||
sum.t === "L"
|
||||
? handlers.left(sum.v)
|
||||
: handlers.right(sum.v);
|
||||
|
||||
export const ModuleSum = {l:[
|
||||
// binary type constructor
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { add, emptySet, forEach } from "../structures/set.js";
|
||||
import { deepEqual } from "../util/util.js";
|
||||
import {inspect} from "node:util";
|
||||
import { compareSlots } from "../compare/versioning.js";
|
||||
|
||||
// UUID -> Value<a> -> Slot<a>
|
||||
export const newSlot = uuid => value => ({
|
||||
|
|
@ -10,39 +11,6 @@ export const newSlot = uuid => 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>
|
||||
export const overwrite = slot => value => ({
|
||||
overwrites: slot,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue