reorganize directory and file structure
This commit is contained in:
parent
1d826ea8d4
commit
48390b8556
99 changed files with 1155 additions and 1629 deletions
28
lib/compare/primitives.js
Normal file
28
lib/compare/primitives.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Total ordering of primitive types
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// The Unit-type has only one instance, which is equal to itself:
|
||||
export const compareUnits = _ => _ => 0;
|
||||
|
||||
// Symbols are encoded as strings
|
||||
export const compareSymbols = a => b => compareStrings(a)(b);
|
||||
11
lib/compare/primitives.types.js
Normal file
11
lib/compare/primitives.types.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||
import { compareBools, compareNumbers, compareSymbols, compareUnits } from "./primitives.js";
|
||||
|
||||
const mkType = getDefaultTypeParser();
|
||||
|
||||
export const ModuleComparePrimitives = [
|
||||
{i: compareNumbers, t: mkType("Double -> Double -> Int")},
|
||||
{i: compareBools , t: mkType("Bool -> Bool -> Int")},
|
||||
{i: compareUnits , t: mkType("Unit -> Unit -> Int")},
|
||||
{i: compareSymbols, t: mkType("SymbolT -> SymbolT -> Int")},
|
||||
];
|
||||
35
lib/compare/registry.js
Normal file
35
lib/compare/registry.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { getInst, getType } from "../primitives/dynamic.js";
|
||||
import { SymbolBool, SymbolChar, SymbolDouble, SymbolInt, SymbolType, SymbolUnit } from "../primitives/primitive_types.js";
|
||||
import { getHumanReadableName } from "../primitives/symbol.js";
|
||||
import { symbolDict, symbolList, symbolProduct, symbolSet, symbolSum } from "../structures/type_constructors.js";
|
||||
import { capitalizeFirstLetter } from "../util/util.js";
|
||||
import { compareBools, compareNumbers, compareStrings, compareUnits } from "./primitives.js";
|
||||
import { compareLists, compareProducts, compareSets, compareSums } from "./structures.js";
|
||||
import { compareTypes } from "./type.js";
|
||||
|
||||
const typeSymbolToCmp = new Map([
|
||||
[SymbolInt , compareNumbers],
|
||||
[SymbolChar , compareStrings],
|
||||
[SymbolDouble , compareNumbers],
|
||||
[SymbolBool , compareBools],
|
||||
[SymbolUnit , compareUnits],
|
||||
[SymbolType , compareTypes],
|
||||
|
||||
// these functions take extra comparison callbacks:
|
||||
[symbolList , compareLists],
|
||||
[symbolProduct , compareProducts],
|
||||
[symbolSum , compareSums],
|
||||
[symbolSet , compareSets],
|
||||
// [symbolDict , compareDicts], TODO
|
||||
]);
|
||||
|
||||
export const makeCompareFn = type => {
|
||||
return type.params.reduce(
|
||||
(acc, cur) => acc(makeCompareFn(cur(type))),
|
||||
typeSymbolToCmp.get(type.symbol)
|
||||
);
|
||||
};
|
||||
|
||||
export const compareDynamic = x => y =>
|
||||
compareTypes(getType(x))(getType(y))
|
||||
|| makeCompareFn(getType(x))(getInst(x))(getInst(y));
|
||||
66
lib/compare/structures.js
Normal file
66
lib/compare/structures.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// Total ordering of composed types
|
||||
|
||||
import { compareNumbers } from "./primitives.js"
|
||||
import { get, length as lengthLs } from "../structures/list.js";
|
||||
import { read, length as lengthSet } from "../structures/set.js";
|
||||
import { newProduct, 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 => {
|
||||
// console.log("compareSums...", x, y)
|
||||
return match(x)
|
||||
(leftValueX => match(y)
|
||||
(leftValueY => compareLeft(leftValueX)(leftValueY)) // both are left
|
||||
((_rightValueY) => {
|
||||
// console.log("x is 'left' and y is 'right' => x < y")
|
||||
return -1;
|
||||
}) // x is 'left' and y is 'right' => x < y
|
||||
)
|
||||
(rightValueX => match(y)
|
||||
(_leftValueY => {
|
||||
// console.log("x is 'right' and y is 'left' => x > y");
|
||||
return 1;
|
||||
}) // x is 'right' and y is 'left' => x > y
|
||||
(rightValueY => compareRight(rightValueX)(rightValueY)) // both are right
|
||||
);
|
||||
};
|
||||
|
||||
// (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
|
||||
return iterate(first(x))(first(y));
|
||||
})();
|
||||
};
|
||||
14
lib/compare/structures.types.js
Normal file
14
lib/compare/structures.types.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||
import { compareLists, compareProducts, compareSets, compareSums } from "./structures.js";
|
||||
|
||||
const mkType = getDefaultTypeParser();
|
||||
|
||||
export const ModuleCompareStructures = [
|
||||
{i: compareLists, t: mkType("∀a: (a -> a -> Int) -> [a] -> [a] -> Int")},
|
||||
|
||||
{i: compareProducts, t: mkType("∀a,b: (a -> a -> Int) -> (b -> b -> Int) -> (a*b) -> (a*b) -> Int")},
|
||||
|
||||
{i: compareSums, t: mkType("∀a,b: (a -> a -> Int) -> (b -> b -> Int) -> (a+b) -> (a+b) -> Int")},
|
||||
|
||||
{i: compareSets, t: mkType("∀a: (a -> a -> Int) -> {a} -> {a} -> Int")},
|
||||
];
|
||||
38
lib/compare/type.js
Normal file
38
lib/compare/type.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { getParams, getSymbol } from "../primitives/type.js";
|
||||
import { compareBools, compareNumbers, compareSymbols } from "./primitives.js";
|
||||
import { compareLists } from "./structures.js";
|
||||
|
||||
const __compareTypes = state => typeX => typeY => {
|
||||
// tagX and tagY: just something unique & deterministic that can serve as JS map key
|
||||
const tagX = state.nextTag++;
|
||||
const tagY = state.nextTag++;
|
||||
state.tagsX.add(tagX);
|
||||
state.tagsY.add(tagY);
|
||||
state.comparing.set(tagX, tagY);
|
||||
return compareSymbols(getSymbol(typeX))(getSymbol(typeY))
|
||||
|| compareLists
|
||||
(paramOfX => paramOfY => {
|
||||
const pX = paramOfX(tagX);
|
||||
const pY = paramOfY(tagY);
|
||||
return compareBools(state.tagsX.has(pX))(state.tagsY.has(pY))
|
||||
|| (() => {
|
||||
if (state.tagsX.has(pX)) {
|
||||
// both sub-types have been visited already in an enclosing call
|
||||
// if they were being compared in the same enclosing call, we assume they are equal!
|
||||
// (we cannot compare them, that would result in endless recursion)
|
||||
return compareNumbers(state.comparing.get(pX))(pY);
|
||||
}
|
||||
// none have been visited -> recursively compare
|
||||
return __compareTypes(state)(pX)(pY);
|
||||
})();
|
||||
})
|
||||
(getParams(typeX))
|
||||
(getParams(typeY));
|
||||
};
|
||||
|
||||
export const compareTypes = typeX => typeY => __compareTypes({
|
||||
tagsX: new Set(),
|
||||
tagsY: new Set(),
|
||||
comparing: new Map(),
|
||||
nextTag: 0,
|
||||
})(typeX)(typeY);
|
||||
8
lib/compare/type.types.js
Normal file
8
lib/compare/type.types.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||
import { compareTypes } from "./type.js";
|
||||
|
||||
const mkType = getDefaultTypeParser();
|
||||
|
||||
export const ModuleCompareTypes = [
|
||||
{i: compareTypes, t: mkType("Type -> Type -> Int")},
|
||||
];
|
||||
35
lib/compare/versioning.js
Normal file
35
lib/compare/versioning.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Total ordering of slots and values (versioning library).
|
||||
// Problem: A Value produced by a transformation can depend on other Values of any type!
|
||||
// So, we cannot statically know the entire type -> resort to dynamic typing for these?
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue