diff --git a/examples/enum.js b/examples/enum.js
index 8ca9301..d58da5d 100644
--- a/examples/enum.js
+++ b/examples/enum.js
@@ -1,4 +1,4 @@
-import { makeCompareFn } from "../lib/compare/registry.js";
+import { makeCompareFn } from "../lib/compare/dynamic.js";
import { Int, Unit } from "../lib/primitives/primitive_types.js";
import { unit } from "../lib/primitives/unit.js";
import { makeConstructors, makeMatchFn } from "../lib/structures/enum.js";
@@ -7,16 +7,18 @@ import { newProduct } from "../lib/structures/product.js";
import { lsType } from "../lib/structures/type_constructors.js";
import { prettyT } from "../lib/util/pretty.js";
+Error.stackTraceLimit = Infinity;
+
const variants = [
- newProduct("price")(Int),
- newProduct("prices")(lsType(_ => Int)),
- newProduct("not_found")(Unit),
+ newProduct("price")(_ => Int),
+ newProduct("prices")(_ => lsType(_ => Int)),
+ newProduct("not_found")(_ => Unit),
];
-const myEnumType = enumType(variants);
+const compatibleNestedSumType = enumType(variants);
console.log("observe the type that was generated:");
-console.log(" ", prettyT(myEnumType));
+console.log(" ", prettyT(compatibleNestedSumType));
const [newPrice, newPrices, newNotFound] = makeConstructors(variants);
@@ -39,7 +41,7 @@ console.log(" ", myEnumToString(price));
console.log(" ", myEnumToString(prices));
console.log(" ", myEnumToString(notFound));
-const compareMyEnum = makeCompareFn(myEnumType);
+const compareMyEnum = makeCompareFn(compatibleNestedSumType);
console.log("observe the generated compare function in action:");
console.log(" should be smaller ->", compareMyEnum(price)(prices));
diff --git a/examples/generics.js b/examples/generics.js
deleted file mode 100644
index c60ba62..0000000
--- a/examples/generics.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import { assign, makeGeneric, unify } from "../lib/generics/generics.js";
-import { prettyGenT } from "../lib/util/pretty.js";
-import { getDefaultTypeParser } from "../lib/parser/type_parser.js";
-
-const mkType = getDefaultTypeParser();
-
-console.log("should be: [Bool] -> Int")
-console.log(prettyGenT(
- unify(
- mkType("∀a: (a -> Int)"),
- makeGeneric(() => mkType("[Bool] -> Int")),
- )
-));
-
-console.log("should be: (Bool -> Bool) -> a");
-console.log(prettyGenT(
- unify(
- mkType("∀a,b: (a -> a) -> b"),
- mkType("∀a: (Bool -> Bool) -> a"),
- )
-));
-
-console.log("should be: [a] -> [a]");
-console.log(prettyGenT(
- assign(
- mkType("∀a,b: (a -> b) -> [a] -> [b]"),
- mkType("∀a: a -> a")
- )
-));
-
-console.log("should be: [Int] -> Int");
-console.log(prettyGenT(
- assign(
- mkType("∀a: (a -> Int) -> [a] -> a"),
- mkType("∀a: a -> a")
- )
-));
diff --git a/examples/parser.js b/examples/parser.js
index 0c5ce7c..e69de29 100644
--- a/examples/parser.js
+++ b/examples/parser.js
@@ -1,16 +0,0 @@
-import { getDefaultTypeParser }from "../lib/parser/type_parser.js";
-import { prettyGenT, prettyT } from "../lib/util/pretty.js";
-
-const parse = getDefaultTypeParser();
-
-console.log(prettyT(parse("Int"))); // Int
-
-console.log(prettyT(parse("Int * Bool"))); // (Int ⨯ Bool)
-
-console.log(prettyT(parse("(((((((Int)))) => ((Bool)))))"))); // (Int => Bool)
-
-console.log(prettyT(parse("#0((Int * #0) + Unit)"))) // #0((Int ⨯ #0) + Unit)
-
-console.log(prettyGenT(parse("∀a: #0((a * #0) + Unit"))); // ∀a: #0((a ⨯ #0) + Unit)
-
-console.log(prettyGenT(parse("∀a,b,c,d: (a*b) + (c*d)"))); // ∀a,b,c,d: ((a ⨯ b) + (c ⨯ d))
diff --git a/examples/recursive_types.js b/examples/recursive_types.js
index fae27f8..6a8c7d2 100644
--- a/examples/recursive_types.js
+++ b/examples/recursive_types.js
@@ -1,8 +1,9 @@
import { compareTypes } from "../lib/compare/type.js";
import { makeGeneric, substitute, unify } from "../lib/generics/generics.js";
import { Double, Int, Unit } from "../lib/primitives/primitive_types.js";
+import { TYPE_VARS } from "../lib/primitives/typevars.js";
import { fnType, lsType, prodType, sumType, setType } from "../lib/structures/type_constructors.js";
-import { prettyGenT, prettyT } from "../lib/util/pretty.js";
+import { prettyT } from "../lib/util/pretty.js";
Error.stackTraceLimit = Infinity;
@@ -28,8 +29,8 @@ const genericLinkedList = makeGeneric(a => makeLinkedList(a));
console.log(prettyT(listOfSetOfSelf)); // #0[{#0}]
console.log(prettyT(linkedListOfInt)); // #0((Int ⨯ #0) + Unit)
-console.log(prettyGenT(genericFunction)); // ∀a,b: (a -> b)
-console.log(prettyGenT(genericLinkedList)); // ∀a: #0((a ⨯ #0) + Unit)
+console.log(prettyT(genericFunction)); // (a -> b)
+console.log(prettyT(genericLinkedList)); // #0((a ⨯ #0) + Unit)
// comparison
@@ -45,16 +46,15 @@ console.log(compareTypes(listOfSetOfSelf)(linkedListOfDouble)) // -1
const genericList = makeGeneric(a => lsType(_ => a));
const intList = lsType(_ => Int);
-console.log(prettyGenT(genericList)); // ∀a: [a]
+console.log(prettyT(genericList)); // [a]
// substitution of type parameters
const substituted = substitute(
- genericList.type,
- new Map([[
- genericList.typeVars.keys().next().value,
- Int,
- ]])
+ genericList,
+ new Map([
+ [TYPE_VARS[0], Int],
+ ])
);
console.log(prettyT(substituted)); // [Int]
@@ -63,16 +63,15 @@ console.log(prettyT(substituted)); // [Int]
console.log("recursive substitution")
console.log(prettyT(substitute(
- genericLinkedList.type,
- new Map([[
- genericLinkedList.typeVars.keys().next().value,
- Int,
- ]])
+ genericLinkedList,
+ new Map([
+ [TYPE_VARS[0], Int],
+ ])
))); // #0((Int ⨯ #0) + Unit)
// unification (simple case)
-const {typeVars, type} = unify(
+const type = unify(
genericList,
makeGeneric(() => intList));
@@ -86,7 +85,7 @@ const unified = unify(
genericLinkedList,
makeGeneric(() => linkedListOfInt));
-console.log(prettyGenT(unified)); // ∀: #0((Int ⨯ #0) + Unit)
+console.log(prettyT(unified)); // #0((Int ⨯ #0) + Unit)
// unification (strange case)
@@ -95,4 +94,4 @@ const unified2 = unify(
genericList,
);
-console.log(prettyGenT(unified2)); // ∀: #0[{#0}]
+console.log(prettyT(unified2)); // #0[{#0}]
diff --git a/examples/versioning.js b/examples/versioning.js
index f4688c0..c9f862d 100644
--- a/examples/versioning.js
+++ b/examples/versioning.js
@@ -1,8 +1,7 @@
-import { pretty } from "../util/pretty.js";
-import { newLiteral, transform, read, getReadDependencies, verifyValue } from "../versioning/value.js";
-import { merge, merge2, newSlot, overwrite } from "../versioning/slot.js";
-import { add, emptySet, RBTreeWrapper } from "../structures/set.js";
-import { compareNumbers } from "../compare/primitives.js";
+import { pretty } from "../lib/util/pretty.js";
+import { newLiteral, transform, read, getReadDependencies, verifyValue } from "../lib/versioning/value.js";
+import { merge, merge2, newSlot, overwrite } from "../lib/versioning/slot.js";
+import { compareNumbers } from "../lib/compare/primitives.js";
const inc = x => x + 1;
diff --git a/lib/compare/registry.js b/lib/compare/dynamic.js
similarity index 52%
rename from lib/compare/registry.js
rename to lib/compare/dynamic.js
index d773d7b..ef40f3c 100644
--- a/lib/compare/registry.js
+++ b/lib/compare/dynamic.js
@@ -1,22 +1,32 @@
import { getInst, getType } from "../primitives/dynamic.js";
-import { SymbolBool, SymbolChar, SymbolDouble, SymbolInt, SymbolType, SymbolUnit } from "../primitives/primitive_types.js";
-import { symbolDict, symbolList, symbolProduct, symbolSet, symbolSum } from "../structures/type_constructors.js";
-import { compareBools, compareNumbers, compareStrings, compareUnits } from "./primitives.js";
-import { compareDicts, compareLists, compareProducts, compareSets, compareSums } from "./structures.js";
+import { SymbolBool, SymbolBottom, SymbolByte, SymbolChar, SymbolDouble, SymbolDynamic, SymbolInt, SymbolUUID, SymbolType, SymbolUnit } from "../primitives/primitive_types.js";
+import { symbolDict, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSum } from "../structures/type_constructors.js";
+import { compareBools, compareNumbers, compareStrings, compareSymbols, compareUnits } from "./primitives.js";
+import { compareDicts, compareFunctions, compareLists, compareProducts, compareSets, compareSums } from "./structures.js";
import { compareTypes } from "./type.js";
+export const compareDynamic = x => y =>
+ compareTypes(getType(x))(getType(y))
+ || makeCompareFn(getType(x))(getInst(x))(getInst(y));
+
const typeSymbolToCmp = new Map([
[SymbolInt , compareNumbers],
- [SymbolChar , compareStrings],
- [SymbolDouble , compareNumbers],
[SymbolBool , compareBools],
+ [SymbolDouble , compareNumbers],
+ [SymbolByte , compareNumbers],
+ [SymbolChar , compareStrings],
[SymbolUnit , compareUnits],
+ [SymbolBottom , _ => _ => { throw new Error("Bottom!"); }],
+ [SymbolUUID , compareSymbols],
+ // [SymbolGenericType, ?] TODO
[SymbolType , compareTypes],
+ [SymbolDynamic, compareDynamic],
// these functions take extra comparison callbacks:
- [symbolList , compareLists],
- [symbolProduct , compareProducts],
+ [symbolFunction, compareFunctions],
[symbolSum , compareSums],
+ [symbolProduct , compareProducts],
+ [symbolList , compareLists],
[symbolSet , compareSets],
[symbolDict , compareDicts],
]);
@@ -27,7 +37,3 @@ export const makeCompareFn = type => {
typeSymbolToCmp.get(type.symbol)
);
};
-
-export const compareDynamic = x => y =>
- compareTypes(getType(x))(getType(y))
- || makeCompareFn(getType(x))(getInst(x))(getInst(y));
diff --git a/lib/compare/dynamic.types.js b/lib/compare/dynamic.types.js
new file mode 100644
index 0000000..c045093
--- /dev/null
+++ b/lib/compare/dynamic.types.js
@@ -0,0 +1,9 @@
+import { getDefaultTypeParser } from "../parser/type_parser.js";
+import { compareDynamic, makeCompareFn } from "./dynamic.js";
+
+const mkType = getDefaultTypeParser();
+
+export const ModuleCompareDynamic = [
+ {i: makeCompareFn , t: mkType("∀a: Type -> a -> a -> Int")},
+ {i: compareDynamic, t: mkType("Dynamic -> Dynamic -> Int")},
+];
\ No newline at end of file
diff --git a/lib/compare/structures.js b/lib/compare/structures.js
index a12c7dc..77c45ba 100644
--- a/lib/compare/structures.js
+++ b/lib/compare/structures.js
@@ -1,12 +1,17 @@
// Total ordering of composed types
-import { compareNumbers } from "./primitives.js"
+import { compareNumbers, compareStrings } from "./primitives.js"
import { get, length as lengthLs } from "../structures/list.js";
import { read as readSet, length as lengthSet, first as firstSet } from "../structures/set.js";
import { read as readDict, length as lengthDict, first as firstDict } from "../structures/dict.js";
import { getLeft, getRight } from "../structures/product.js";
import { match } from "../structures/sum.js";
+export const compareFunctions = _compareInput => _compareOutput => x => y => {
+ // dirty but does the job
+ return compareStrings(x.toString())(y.toString());
+}
+
export const compareLists = compareElems => x => y => {
return compareNumbers(lengthLs(x))(lengthLs(y))
|| (() => {
diff --git a/lib/generics/generics.js b/lib/generics/generics.js
index d8ee7e4..d6a4e5b 100644
--- a/lib/generics/generics.js
+++ b/lib/generics/generics.js
@@ -1,64 +1,57 @@
-import { eqType } from "../primitives/type.js";
+import { eqType, getSymbol } from "../primitives/type.js";
import { zip } from "../util/util.js";
import { pretty, prettyT } from '../util/pretty.js';
+import { isTypeVar, TYPE_VARS } from "../primitives/typevars.js";
-// constructor for generic types
+// helper for creating generic types
// for instance, the type:
// ∀a: a -> a -> Bool
// is created by
// makeGeneric(a => fnType(() => a)(() => fnType(() => a)(() => Bool)))
export const makeGeneric = callback => {
// type variables to make available:
- const typeVars = ['a', 'b', 'c', 'd', 'e'].map(Symbol);
- const type = callback(...typeVars);
- return onlyOccurring(type, new Set(typeVars));
+ const type = callback(...TYPE_VARS);
+ return type;
};
-export const onlyOccurring = (type, typeVars) => ({
- typeVars: occurring(type, typeVars),
- type,
-});
-
-const __occurring = state => typeVars => type => {
- if (typeVars.has(type)) {
- return new Set([type]);
+const _occurring = stack => type => {
+ if (isTypeVar(type)) {
+ return new Set([getSymbol(type)]);
}
- const tag = state.nextTag++;
- state.seen.add(tag);
+ const tag = stack.length;
+ const newStack = [...stack, tag];
return new Set(type.params.flatMap(p => {
const innerType = p(tag);
- if (state.seen.has(innerType)) {
+ if (newStack.includes(innerType)) {
return []; // no endless recursion!
}
- return [...__occurring(state)(typeVars)(innerType)];
+ return [..._occurring(newStack)(innerType)];
}));
};
-// From the given set of type variables, return only those that occur in the given type.
-export const occurring = (type, typeVars) => {
- return __occurring({nextTag:0, seen: new Set()})(typeVars)(type);
-};
+// Get set of type variables in type.
+export const occurring = _occurring([]);
// Merge 2 substitution-mappings, uni-directional.
const mergeOneWay = (m1, m2) => {
const m1copy = new Map(m1);
const m2copy = new Map(m2);
- for (const [var1, typ1] of m1copy.entries()) {
- if (m2copy.has(typ1)) {
+ for (const [symbol1, typ1] of m1copy) {
+ if (m2copy.has(getSymbol(typ1))) {
// typ1 is a typeVar for which we also have a substitution
// -> fold substitutions
- m1copy.set(var1, m2.get(typ1));
- m2copy.delete(typ1);
- return [false, m1copy, m2copy, new Set([typ1])];
+ m1copy.set(symbol1, m2.get(getSymbol(typ1)));
+ m2copy.delete(getSymbol(typ1));
+ return [false, m1copy, m2copy];
}
}
- return [true, m1copy, m2copy, new Set()]; // stable
+ return [true, m1copy, m2copy]; // stable
};
const checkConflict = (m1, m2) => {
- for (const [var1, typ1] of m1) {
- if (m2.has(var1)) {
- const other = m2.get(var1);
+ for (const [symbol1, typ1] of m1) {
+ if (m2.has(symbol1)) {
+ const other = m2.get(symbol1);
if (!eqType(typ1, other)) {
throw new Error(`conflicting substitution: ${pretty(typ1)}vs. ${pretty(other)}`);
}
@@ -73,21 +66,17 @@ export const mergeTwoWay = (m1, m2) => {
// checkConflict(m2, m1); // <- don't think this is necessary...
// actually merge
let stable = false;
- let deletions = new Set();
while (!stable) {
- let d;
// notice we swap m2 and m1, so the rewriting can happen both ways:
- [stable, m2, m1, d] = mergeOneWay(m1, m2);
- deletions = deletions.union(d);
+ [stable, m2, m1] = mergeOneWay(m1, m2);
}
- const result = {
- substitutions: new Map([...m1, ...m2]),
- deletions, // deleted type variables
- };
+ const result = new Map([...m1, ...m2]);
// console.log("mergeTwoWay result =", result);
return result;
};
+export class UnifyError extends Error {}
+
// Thanks to Hans for pointing out that this algorithm exactly like "Unification" in Prolog (hence the function name):
// https://www.dai.ed.ac.uk/groups/ssp/bookpages/quickprolog/node12.html
//
@@ -95,35 +84,29 @@ export const mergeTwoWay = (m1, m2) => {
// typeVars: all the type variables in both fType and aType
// fType, aType: generic types to unify
// fStack, aStack: internal use.
-const __unify = (typeVars, fType, aType, fStack=[], aStack=[]) => {
+const __unify = (fType, aType, fStack=[], aStack=[]) => {
// console.log("__unify", {typeVars, fType: prettyT(fType), aType: prettyT(aType), fStack, aStack});
- if (typeVars.has(fType)) {
+ if (isTypeVar(fType)) {
// simplest case: formalType is a type paramater
// => substitute with actualType
// console.log(`assign ${prettyT(aType)} to ${prettyT(fType)}`);
return {
- substitutions: new Map([[fType, aType]]),
- genericType: {
- typeVars: typeVars.difference(new Set([fType])),
- type: aType,
- },
+ substitutions: new Map([[getSymbol(fType), aType]]),
+ type: aType,
};
}
- if (typeVars.has(aType)) {
+ if (isTypeVar(aType)) {
// same as above, but in the other direction
// console.log(`assign ${prettyT(fType)} to ${prettyT(aType)}`);
return {
- substitutions: new Map([[aType, fType]]),
- genericType: {
- typeVars: typeVars.difference(new Set([aType])),
- type: fType,
- },
+ substitutions: new Map([[getSymbol(aType), fType]]),
+ type: fType,
};
}
// recursively unify
if (fType.symbol !== aType.symbol) {
- throw new Error(`cannot unify ${prettyT(fType)} and ${prettyT(aType)}`);
+ throw new UnifyError(`cannot unify ${prettyT(fType)} and ${prettyT(aType)}`);
}
const fTag = fStack.length;
@@ -137,106 +120,79 @@ const __unify = (typeVars, fType, aType, fStack=[], aStack=[]) => {
// type recursively points to an enclosing type that we've already seen
if (fStack[fParam] !== aStack[aParam]) {
// note that both are also allowed not to be mapped (undefined)
- throw new Error("cannot unify: types differ in their recursion");
+ throw new UnifyError("cannot unify: types differ in their recursion");
}
if (fStack[fParam] !== undefined) {
const type = fStack[fParam];
return () => ({
substitutions: new Map(),
- genericType: {
- typeVars,
- type,
- },
+ type,
});
}
- return parent => __unify(typeVars, fParam, aParam,
+ return parent => __unify(fParam, aParam,
[...fStack, parent],
[...aStack, parent]);
});
const unifiedParams = unifications.map(getParam => {
- return parent => getParam(parent).genericType.type;
+ return parent => getParam(parent).type;
});
- const type = {
- symbol: fType.symbol,
- params: unifiedParams,
- };
- const [unifiedSubstitutions, unifiedTypeVars] = unifications.reduce((acc, getParam) => {
+ const unifiedSubstitutions = unifications.reduce((acc, getParam) => {
const self = Symbol(); // dirty, just need something unique
- const {substitutions, deletions} = mergeTwoWay(acc[0], getParam(self).substitutions);
- return [substitutions, acc[1]
- .difference(substitutions)
- .difference(deletions)];
- }, [new Map(), typeVars]);
+ const paramSubstitutions = getParam(self).substitutions;
+ const substitutions = mergeTwoWay(acc, paramSubstitutions);
+ return substitutions;
+ }, new Map());
return {
substitutions: unifiedSubstitutions,
- genericType: {
- typeVars: unifiedTypeVars,
- type,
+ type: {
+ symbol: fType.symbol,
+ params: unifiedParams,
},
};
};
-export const unify = (fGenericType, aGenericType) => {
- let allTypeVars;
- [allTypeVars, fGenericType, aGenericType] = safeUnionTypeVars(fGenericType, aGenericType);
- const {genericType, substitutions} = __unify(allTypeVars, fGenericType.type, aGenericType.type);
+export const unify = (fType, aType) => {
+ [fType, aType] = recomputeTypeVars([fType, aType]);
+ const {type, substitutions} = __unify(fType, aType);
// console.log('unification complete! substitutions:', substitutions);
- return recomputeTypeVars(genericType);
+ return recomputeTypeVars([type])[0];
};
export const substitute = (type, substitutions, stack=[]) => {
// console.log('substitute...', {type, substitutions, stack});
- return substitutions.get(type)
+ return substitutions.get(getSymbol(type))
|| {
- symbol: type.symbol,
+ symbol: getSymbol(type),
params: type.params.map(getParam => parent => {
const param = getParam(stack.length);
- const have = stack[param];
- return (have !== undefined)
- ? have
- : substitute(param, substitutions, [...stack, parent]);
+ return stack[param]
+ || substitute(param, substitutions, [...stack, parent]);
}),
};
};
-export const assign = (genFnType, paramType) => {
- let allTypeVars;
- [allTypeVars, genFnType, paramType] = safeUnionTypeVars(genFnType, paramType);
- const [inType, outType] = genFnType.type.params;
- const {substitutions} = __unify(allTypeVars, inType(genFnType.type), paramType.type);
- const substitutedOutType = substitute(outType(genFnType.type), substitutions);
- return recomputeTypeVars(onlyOccurring(substitutedOutType, allTypeVars));
+export const assignFn = (funType, paramType) => {
+ [funType, paramType] = recomputeTypeVars([funType, paramType]);
+ // console.log(prettyT(funType), prettyT(paramType));
+ const [inType, outType] = funType.params.map(p => p(funType));
+ const {substitutions} = __unify(inType, paramType);
+ // console.log(substitutions, prettyT(outType));
+ const substitutedFnType = substitute(outType, substitutions);
+ return recomputeTypeVars([substitutedFnType])[0];
};
-export const assignFn = (genFnType, paramType) => {
- let allTypeVars;
- [allTypeVars, genFnType, paramType] = safeUnionTypeVars(genFnType, paramType);
- const [inType] = genFnType.type.params;
- const {substitutions} = __unify(allTypeVars, inType, paramType.type);
- const substitutedFnType = substitute(genFnType.type, substitutions);
- return recomputeTypeVars(onlyOccurring(substitutedFnType, allTypeVars));
-};
-
-export const recomputeTypeVars = (genType) => {
- const newTypeVars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'].map(Symbol);
+// Ensures that no type variables overlap
+export const recomputeTypeVars = types => {
let nextIdx = 0;
- const subst = new Map();
- for (const typeVarA of genType.typeVars) {
- subst.set(typeVarA, newTypeVars[nextIdx++]);
- }
- const substType = {
- typeVars: new Set(subst.values()),
- type: substitute(genType.type, subst),
- };
- return substType;
-};
-
-export const safeUnionTypeVars = (genTypeA, genTypeB) => {
- const substTypeA = recomputeTypeVars(genTypeA);
- const substTypeB = recomputeTypeVars(genTypeB);
- const allTypeVars = substTypeA.typeVars.union(substTypeB.typeVars);
- return [allTypeVars, substTypeA, substTypeB];
-};
+ return types.map(type => {
+ const substitutions = new Map();
+ const typeVars = occurring(type);
+ for (const typeVar of typeVars) {
+ substitutions.set(typeVar, TYPE_VARS[nextIdx++]);
+ }
+ return substitute(type, substitutions);
+ });
+}
diff --git a/lib/parser/type_parser.js b/lib/parser/type_parser.js
index 3ba9b07..9378f1f 100644
--- a/lib/parser/type_parser.js
+++ b/lib/parser/type_parser.js
@@ -1,7 +1,10 @@
// A simple, hacked-together recursive parser for types.
-import { Bool, Char, Double, Int, SymbolT, Type, Unit } from "../primitives/primitive_types.js";
+import { Bool, Char, Double, Int, UUID, Type, Unit } from "../primitives/primitive_types.js";
import { Dynamic } from "../primitives/primitive_types.js";
+import { getHumanReadableName } from "../primitives/symbol.js";
+import { getSymbol } from "../primitives/type.js";
+import { TYPE_VARS } from "../primitives/typevars.js";
import { dictType, fnType, lsType, prodType, sumType } from "../structures/type_constructors.js";
import { setType } from "../structures/type_constructors.js";
@@ -11,12 +14,6 @@ export const makeTypeParser = ({
extraBracketOperators=[],
extraInfixOperators=[],
}) => {
- const a = Symbol('a');
- const b = Symbol('b');
- const c = Symbol('c');
- const d = Symbol('d');
- const e = Symbol('e');
-
const primitives = new Map([
['Int', Int],
['Double', Double],
@@ -27,12 +24,9 @@ export const makeTypeParser = ({
['Unit', Unit],
['Type', Type],
['Dynamic', Dynamic],
- ['SymbolT', SymbolT],
- ['a', a],
- ['b', b],
- ['c', c],
- ['d', d],
- ['e', e],
+ ['UUID', UUID],
+
+ ...TYPE_VARS.map(type => [getHumanReadableName(getSymbol(type)), type]),
...extraPrimitives,
]);
@@ -41,11 +35,7 @@ export const makeTypeParser = ({
['(', [')', null]],
['[', [']', lsType]],
['{', ['}', setType]],
-
- // can only occur at beginning
- // we use these to extract the type variables
- ['∀', [':', null]],
-
+
...extraBracketOperators,
]);
@@ -187,13 +177,6 @@ export const makeTypeParser = ({
const parse = expr => {
const tokens = tokenize(expr);
- if (tokens[0] === '∀') {
- // generic type
- const [typeVarTokens, _, rest] = consumeGroup(tokens);
- const typeVars = [].concat(__parse(typeVarTokens))
- const type = __parse(rest);
- return { typeVars, type };
- }
return __parse(tokens);
}
diff --git a/lib/primitives/primitive_types.js b/lib/primitives/primitive_types.js
index c4b7f88..8e2f95d 100644
--- a/lib/primitives/primitive_types.js
+++ b/lib/primitives/primitive_types.js
@@ -9,10 +9,9 @@ export const SymbolByte = "Byte__bf9e8453cd554e81971880ba33dc9f27";
export const SymbolChar = "Char__e47159519d3345119336b751fc8da1de";
export const SymbolUnit = "Unit__a70ca021c32a4036a594d332aedfb029";
export const SymbolBottom = "⊥__95beece951bc457781be8c5481d35dcc";
-export const SymbolSymbol = "Symbol__f67c077430e04e4fa40ed2e2b2a3040d";
+export const SymbolUUID = "UUID__f67c077430e04e4fa40ed2e2b2a3040d";
export const SymbolType = "Type__fdbea309d66f49b483b0dd4ceb785f7d";
export const SymbolTop = "⊤__38709c3c0039468782103256d4730d1f";
-export const SymbolGenericType = "GenericType__e9d8010b82f64206afa83d0c163df5d2";
export const SymbolDynamic = "Dynamic__3c16c415dba94228ada37dc9d446f54f";
export const Int = makeTypeConstructor(SymbolInt)(0);
@@ -27,13 +26,11 @@ export const Unit = makeTypeConstructor(SymbolUnit)(0);
// Bottom type has no instances.
export const Bottom = makeTypeConstructor(SymbolBottom)(0);
-export const SymbolT = makeTypeConstructor(SymbolSymbol)(0);
+export const UUID = makeTypeConstructor(SymbolUUID)(0);
// Types are typed by Top
export const Type = makeTypeConstructor(SymbolType)(0);
-export const GenericType = makeTypeConstructor(SymbolGenericType)(0);
-
// Everything is typed by Top
export const Top = makeTypeConstructor(SymbolTop)(0);// A type-link, connecting a value to its Type.
diff --git a/lib/primitives/primitive_types.types.js b/lib/primitives/primitive_types.types.js
index c5a49d1..6bc2bca 100644
--- a/lib/primitives/primitive_types.types.js
+++ b/lib/primitives/primitive_types.types.js
@@ -1,18 +1,17 @@
-import { SymbolInt, SymbolT, SymbolBool, SymbolDouble, SymbolByte, SymbolChar, SymbolUnit, SymbolBottom, SymbolSymbol, SymbolType, SymbolGenericType, SymbolTop, Type, Int, Bool, Double, Byte, Char, Unit, Bottom, GenericType, Top, SymbolDynamic, Dynamic } from "./primitive_types.js";
+import { SymbolInt, UUID, SymbolBool, SymbolDouble, SymbolByte, SymbolChar, SymbolUnit, SymbolBottom, SymbolUUID, SymbolType, SymbolTop, Type, Int, Bool, Double, Byte, Char, Unit, Bottom, Top, SymbolDynamic, Dynamic } from "./primitive_types.js";
export const ModulePrimitiveSymbols = [
- { i: SymbolInt , t: SymbolT },
- { i: SymbolBool , t: SymbolT },
- { i: SymbolDouble , t: SymbolT },
- { i: SymbolByte , t: SymbolT },
- { i: SymbolChar , t: SymbolT },
- { i: SymbolUnit , t: SymbolT },
- { i: SymbolBottom , t: SymbolT },
- { i: SymbolSymbol , t: SymbolT },
- { i: SymbolType , t: SymbolT },
- { i: SymbolGenericType , t: SymbolT },
- { i: SymbolTop , t: SymbolT },
- { i: SymbolDynamic , t: SymbolT },
+ { i: SymbolInt , t: UUID },
+ { i: SymbolBool , t: UUID },
+ { i: SymbolDouble , t: UUID },
+ { i: SymbolByte , t: UUID },
+ { i: SymbolChar , t: UUID },
+ { i: SymbolUnit , t: UUID },
+ { i: SymbolBottom , t: UUID },
+ { i: SymbolUUID , t: UUID },
+ { i: SymbolType , t: UUID },
+ { i: SymbolTop , t: UUID },
+ { i: SymbolDynamic , t: UUID },
];
export const ModulePrimitiveTypes = [
@@ -23,9 +22,8 @@ export const ModulePrimitiveTypes = [
{ i: Char , t: Type },
{ i: Unit , t: Type },
{ i: Bottom , t: Type },
- { i: SymbolT , t: Type },
+ { i: UUID , t: Type },
{ i: Type , t: Type },
- { i: GenericType , t: Type },
{ i: Top , t: Type },
{ i: Dynamic , t: Type },
];
diff --git a/lib/primitives/typevars.js b/lib/primitives/typevars.js
new file mode 100644
index 0000000..12a854a
--- /dev/null
+++ b/lib/primitives/typevars.js
@@ -0,0 +1,43 @@
+import { makeTypeConstructor } from "../meta/type_constructor.js";
+import { memoize } from "../util/util.js";
+import { getSymbol } from "./type.js";
+
+export const UNBOUND_SYMBOLS = [
+ "a__00000000000000000000000000000000",
+ "b__00000000000000000000000000000000",
+ "c__00000000000000000000000000000000",
+ "d__00000000000000000000000000000000",
+ "e__00000000000000000000000000000000",
+ "f__00000000000000000000000000000000",
+ "g__00000000000000000000000000000000",
+ "h__00000000000000000000000000000000",
+ "i__00000000000000000000000000000000",
+ "j__00000000000000000000000000000000",
+ "k__00000000000000000000000000000000",
+ "l__00000000000000000000000000000000",
+ "m__00000000000000000000000000000000",
+ "n__00000000000000000000000000000000",
+ "o__00000000000000000000000000000000",
+ "p__00000000000000000000000000000000",
+ "q__00000000000000000000000000000000",
+ "r__00000000000000000000000000000000",
+ "s__00000000000000000000000000000000",
+ "t__00000000000000000000000000000000",
+ "u__00000000000000000000000000000000",
+ "v__00000000000000000000000000000000",
+ "w__00000000000000000000000000000000",
+ "x__00000000000000000000000000000000",
+ "y__00000000000000000000000000000000",
+ "z__00000000000000000000000000000000",
+];
+
+// Type variables are just like nominal types.
+export const TYPE_VARS = UNBOUND_SYMBOLS.map(symbol => makeTypeConstructor(symbol)(0));
+
+// Turn list into Set for faster lookup.
+const unbound_set = memoize(() => new Set(UNBOUND_SYMBOLS));
+
+// What makes a type variable a type variable, is its symbol occurring in the above list.
+export const isTypeVar = type => {
+ return unbound_set().has(getSymbol(type));
+};
diff --git a/lib/structures/enum.js b/lib/structures/enum.js
index cfc6e3c..0af542b 100644
--- a/lib/structures/enum.js
+++ b/lib/structures/enum.js
@@ -11,7 +11,7 @@ const eatParameters = (numParams, result) => {
export const makeMatchFn = variants => {
if (variants.length === 0) {
- return undefined;
+ throw new Error("Bottom!");
}
const [_, ...remainingVariants] = variants;
return sum => handler => {
@@ -34,4 +34,4 @@ export const makeConstructors = variants => {
...makeConstructors(remainingVariants).map(ctor =>
({[ctor.name]: val => newRight(ctor(val))}[ctor.name])),
];
-}
+};
diff --git a/lib/structures/enum.types.js b/lib/structures/enum.types.js
index 21436d9..bb17875 100644
--- a/lib/structures/enum.types.js
+++ b/lib/structures/enum.types.js
@@ -1,6 +1,10 @@
-import { Bottom } from "../primitives/primitive_types.js";
+import { makeCompareFn } from "../compare/dynamic.js";
+import { makeGeneric } from "../generics/generics.js";
+import { newDynamic } from "../primitives/dynamic.js";
+import { Bottom, Type } from "../primitives/primitive_types.js";
+import { makeConstructors, makeMatchFn } from "./enum.js";
import { getRight } from "./product.js";
-import { sumType } from "./type_constructors.js";
+import { fnType, sumType } from "./type_constructors.js";
// 'variants' is an array of (name: string, type: Type) pairs.
// e.g., the list of variants:
@@ -10,11 +14,67 @@ import { sumType } from "./type_constructors.js";
// results in the type:
// (Int | ([Int] | (Unit | ⊥)))
-export const enumType = variants => {
+const _enumType = rootSelf => variants => {
+ // console.log("enumType..", variants);
if (variants.length === 0) {
return Bottom; // empty enum is equal to Bottom-type (cannot be instantiated)
}
const [variant, ...rest] = variants;
const variantType = getRight(variant);
- return sumType(() => variantType)(() => enumType(rest));
+ return sumType
+ (self => {
+ // console.log("requested left type (of enumType)")
+ return variantType(rootSelf || self);
+ })
+ (self => {
+ // console.log("requested right type (of enumType)")
+ return _enumType(self)(rest)
+ });
};
+
+export const enumType = _enumType(null);
+
+export const makeConstructorTypes = type => variants => {
+ return variants.map(variant => {
+ const variantType = getRight(variant);
+ return fnType(_ => variantType)(_ => type);
+ });
+}
+
+export const makeMatchFnType = type => variants => {
+ return makeGeneric(resultType =>
+ fnType
+ (_ => type)
+ (_ => handlerType(resultType)(type)(variants)));
+}
+
+const handlerType = resultType => type => variants => {
+ if (variants.length === 0) {
+ return resultType;
+ }
+ const [variant, ...rest] = variants;
+ const variantType = getRight(variant);
+ return fnType
+ (_ => fnType(_ => variantType)(_ => resultType)), // handler
+ (_ => handlerType(resultType)(type)(rest)); // rest
+}
+
+export const makeModuleEnum = type => variants => {
+ const ctors = makeConstructors(variants);
+ const ctorTypes = makeConstructorTypes(type)(variants);
+ const matchFn = makeMatchFn(variants);
+ const matchFnType = makeMatchFnType(type)(variants);
+ const module = [
+ // newDynamic(type)(Type),
+
+ // constructors:
+ ...zip(ctors, ctorTypes)
+ .map(([ctor, ctorType]) => newDynamic(ctor)(ctorType)),
+
+ // match-function:
+ newDynamic(matchFn, matchFnType),
+
+ // compare-function:
+ newDynamic(makeCompareFn(enumType(variants)))
+ ];
+}
\ No newline at end of file
diff --git a/lib/structures/set.js b/lib/structures/set.js
index 46ce176..a535de6 100644
--- a/lib/structures/set.js
+++ b/lib/structures/set.js
@@ -11,6 +11,15 @@ export const add = set => key => set.tree.get(key) === true ? set : new RBTreeWr
export const remove = set => key => new RBTreeWrapper(set.tree.remove(key));
export const length = set => set.tree.length;
+export const fold = set => callback => initial => {
+ let acc = initial;
+ let iter = set.tree.begin;
+ while (iter !== undefined && iter.valid) {
+ acc = callback(acc, iter.key);
+ }
+ return acc;
+};
+
export const first = set => set.tree.begin;
export const last = set => set.tree.end;
diff --git a/lib/structures/set.types.js b/lib/structures/set.types.js
index 20e39da..32fe7af 100644
--- a/lib/structures/set.types.js
+++ b/lib/structures/set.types.js
@@ -1,6 +1,6 @@
import { makeTypeParser } from "../parser/type_parser.js";
import { makeTypeConstructor } from "../meta/type_constructor.js";
-import { emptySet, has, add, remove, length, first, read, last } from "./set.js";
+import { emptySet, has, add, remove, length, first, read, last, fold } from "./set.js";
const setIterator = makeTypeConstructor('SetIterator__f6b0ddd78ed41c58e5a442f2681da011')(1);
@@ -14,6 +14,7 @@ export const ModuleSet = [
{ i: add , t: mkType("∀a: {a} -> a -> {a}")},
{ i: remove , t: mkType("∀a: {a} -> a -> {a}")},
{ i: length , t: mkType("∀a: {a} -> Int")},
+ { i: fold , t: mkType("∀a,b: {a} -> (b -> a -> b) -> b")},
{ i: first , t: mkType("∀a: {a} -> ")},
{ i: last , t: mkType("∀a: {a} -> ")},
{ i: read , t: mkType("∀a: -> (Unit + (a * ))")},
diff --git a/lib/structures/struct.types.js b/lib/structures/struct.types.js
index 2946f65..88b149c 100644
--- a/lib/structures/struct.types.js
+++ b/lib/structures/struct.types.js
@@ -13,40 +13,56 @@ import { fnType, prodType } from "./type_constructors.js";
// [{l: "x", r: Double}, {l: "y", r: Double}]
// results in the type (Double × (Double × Unit))
-export const structType = fieldTypes => {
- if (fieldTypes.length === 0) {
+export const structType = (fields, rootSelf) => {
+ // console.log("structType..", fields);
+ if (fields.length === 0) {
return Unit;
}
- const [fieldType, ...rest] = fieldTypes;
- return prodType(_ => fieldType)(_ => structType(rest));
+ const [field, ...rest] = fields;
+ const fieldType = getRight(field);
+ return prodType
+ (self => {
+ // console.log("requested left type (of structType)")
+ return fieldType(rootSelf || self);
+ })
+ (self => {
+ // console.log("requested right type (of structType)")
+ return structType(rest, self);
+ });
};
-export const makeConstructorType = fieldTypes => {
- if (fieldTypes.length === 0) {
- return structType(fieldTypes);
+export const makeConstructorType = type => fields => {
+ if (fields.length === 0) {
+ return type;
+ // return structType(fields);
}
- const [fieldType, ...rest] = fieldTypes;
- return fnType(_ => fieldType)(_ => makeConstructorType(rest));
+ const [field, ...rest] = fields;
+ const fieldType = getRight(field);
+ return fnType(_ => fieldType)(_ => makeConstructorType(type, rest));
};
-export const makeGettersTypes = fieldTypes => {
- const type = structType(fieldTypes);
- return fieldTypes.map(fieldType => {
+export const makeGettersTypes = type => fields => {
+ // const type = structType(fields);
+ return fields.map(field => {
+ const fieldType = getRight(field);
return fnType(_ => type)(_ => fieldType);
});
};
-export const makeModuleStruct = fields => {
+export const makeModuleStruct = type => fields => {
const fieldNames = map(fields)(getLeft);
- const fieldTypes = map(fields)(getRight);
- const type = structType(fieldTypes);
+ // const type = structType(fields);
const ctor = makeConstructor(fields.length);
- const ctorType = makeConstructorType(fieldTypes);
- const getterTypes = makeGettersTypes(fieldTypes);
+ const ctorType = makeConstructorType(fields);
+ const getterTypes = makeGettersTypes(fields);
const getters = makeGetters(fieldNames);
const module = [
- {i: type, t: Type},
+ // {i: type, t: Type},
+
+ // constructor
{i: ctor, t: ctorType},
+
+ // getters:
...zip(getters, getterTypes)
.map(([getter, getterType]) => newDynamic(getter)(getterType)),
];
diff --git a/lib/structures/type_constructors.types.js b/lib/structures/type_constructors.types.js
index 04c6eb6..81d2c03 100644
--- a/lib/structures/type_constructors.types.js
+++ b/lib/structures/type_constructors.types.js
@@ -1,16 +1,16 @@
import { getDefaultTypeParser } from "../parser/type_parser.js";
-import { SymbolT } from "../primitives/primitive_types.js";
+import { UUID } from "../primitives/primitive_types.js";
import { dictType, fnType, lsType, prodType, setType, sumType, symbolDict, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSum } from "./type_constructors.js";
const mkType = getDefaultTypeParser();
export const ModuleStructuralSymbols = [
- { i: symbolSet , t: SymbolT },
- { i: symbolList , t: SymbolT },
- { i: symbolProduct , t: SymbolT },
- { i: symbolSum , t: SymbolT },
- { i: symbolDict , t: SymbolT },
- { i: symbolFunction , t: SymbolT },
+ { i: symbolSet , t: UUID },
+ { i: symbolList , t: UUID },
+ { i: symbolProduct , t: UUID },
+ { i: symbolSum , t: UUID },
+ { i: symbolDict , t: UUID },
+ { i: symbolFunction , t: UUID },
];
export const ModuleTypeConstructors = [
diff --git a/lib/util/pretty.js b/lib/util/pretty.js
index b8e09cf..a1d0d1f 100644
--- a/lib/util/pretty.js
+++ b/lib/util/pretty.js
@@ -1,36 +1,30 @@
import { inspect } from 'node:util';
import { symbolDict, symbolFunction, symbolList, symbolProduct, symbolSum } from '../structures/type_constructors.js';
import { symbolSet } from "../structures/type_constructors.js";
-import { mapRecursiveStructure } from './util.js';
import { getHumanReadableName } from '../primitives/symbol.js';
+import { getSymbol } from '../primitives/type.js';
export function pretty(obj) {
return inspect(obj, { colors: true, depth: null, breakLength: 120 });
}
// Pretty print Type
-export const prettyT = type => {
- return mapRecursiveStructure(([type, m, seen], map) => {
- if (typeof type === "symbol") {
- // type variable
- return type.description;
- }
- // if (type.params === undefined) {
- // throw new Error("parameter is not a Type ... did you mean to call prettyGenT instead?")
- // }
- if (!m.has(type)) {
- m.add(type); // next time we encounter this type, we'll only render a placeholder
- const params = type.params.map(p => map([p(type), m, seen])());
- // if while visiting the children, we encountered ourselves, add annotation:
- const annot = seen.has(type) ? seen.get(type) : ``;
- return renderType(type.symbol, annot, params);
- }
- if (!seen.has(type)) {
- seen.set(type, `#${seen.size}`);
- }
- return seen.get(type);
- })([type, new Set(), new Map()])();
-};
+export const _prettyT = (depth, tags) => type => {
+ if (typeof type === 'number' && type < depth) {
+ // we've already seen this type, so we'll tag it
+ // we mutate tags in-place so our parent type can see it
+ const hashTag = `#${tags.size}`;
+ // upper level will be tagged:
+ tags.set(type, hashTag);
+ // and this level is entirely replaced by tag:
+ return hashTag;
+ }
+ const params = type.params.map(p => _prettyT(depth+1, tags)(p(depth)));
+ const annot = tags.get(depth) || '';
+ return renderType(getSymbol(type), annot, params);
+}
+
+export const prettyT = type => _prettyT(0, new Map())(type);
const renderType = (symbol, annot, params) => {
return {
@@ -42,8 +36,3 @@ const renderType = (symbol, annot, params) => {
[symbolDict] : `${annot}(${params[0]} => ${params[1]})`,
}[symbol] || getHumanReadableName(symbol);
};
-
-// Pretty print GenericType
-export const prettyGenT = genericType => {
- return `∀${[...genericType.typeVars].map(prettyT).sort((a, b) => a.localeCompare(b)).join(",")}: ${prettyT(genericType.type)}`;
-};
diff --git a/lib/util/util.js b/lib/util/util.js
index 4e401ca..48c4f01 100644
--- a/lib/util/util.js
+++ b/lib/util/util.js
@@ -25,3 +25,15 @@ const _mapRecursiveStructure = mapping => transform => root => {
};
export const mapRecursiveStructure = _mapRecursiveStructure(new Map());
+
+export const memoize = callback => {
+ let called = false
+ let result;
+ return () => {
+ if (!called) {
+ result = callback();
+ called = true;
+ }
+ return result;
+ };
+};
diff --git a/lib/versioning/slot.js b/lib/versioning/slot.js
index 49cd217..d5561b1 100644
--- a/lib/versioning/slot.js
+++ b/lib/versioning/slot.js
@@ -1,25 +1,31 @@
-import { add, emptySet, forEach } from "../../structures/set.js";
+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 -> Slot
-export const newSlot = uuid => value => ({
- overwrites: uuid,
- value,
- depth: 1,
+export const newSlot = uuid => ({
+ kind: "new",
+ uuid,
+ depth: 0,
[inspect.custom]: (depth, options, inspect) => `newSlot(${inspect(uuid)}, ${inspect(value)})`,
});
-// Slot -> Value -> Slot
export const overwrite = slot => value => ({
+ kind: "overwrite",
overwrites: slot,
value,
depth: slot.depth + 1,
// [inspect.custom]: (depth, options, inspect) => `overwrite(${inspect(slot)}, ${inspect(value)})`,
});
+const slotsEqual = slotA => slotB => {
+
+}
+
const findLCA = slotA => slotB => {
+ if (slotA.depth === slotB.depth) {
+
+ }
if (deepEqual(slotA, slotB)) {
return slotA;
}
@@ -31,7 +37,6 @@ const findLCA = slotA => slotB => {
}
};
-// Slot -> Slot -> MergeResult
export const merge = compareElems => slotA => slotB => {
const lca = findLCA(slotA)(slotB);
if (lca === undefined) {
@@ -49,7 +54,6 @@ export const merge = compareElems => slotA => slotB => {
// return new Set([slotA, slotB]);
};
-// MergeResult -> MergeResult -> MergeResult
export const merge2 = compareElems => mA => mB => {
let result = emptySet(compareSlots(compareElems));
forEach(mA)(slotOfA => {
diff --git a/lib/versioning/types.js b/lib/versioning/types.js
index ac55fb0..a4b0284 100644
--- a/lib/versioning/types.js
+++ b/lib/versioning/types.js
@@ -1,27 +1,47 @@
-import { Dynamic } from "../primitives/dynamic.js";
-import { Int } from "../primitives/types.js";
-import { enumType } from "../structures/enum.js";
+import { makeGeneric } from "../generics/generics.js";
+import { makeTypeConstructor } from "../meta/type_constructor.js";
+import { Int, UUID } from "../primitives/primitive_types.js";
+import { enumType, makeModuleEnum } from "../structures/enum.types.js";
import { newProduct } from "../structures/product.js";
-import { structType } from "../structures/struct.js";
-import { prodType } from "../structures/types.js";
+import { structType } from "../structures/struct.types.js";
import { prettyT } from "../util/pretty.js";
-const Slot = structType([
- newProduct("value")(() => Value),
- newProduct("depth")(() => Int),
- newProduct("overwrites")(() => Slot),
+const Slot = makeTypeConstructor("Slot__318d1c1a9336c141336c461c6a3207b0")(1);
+const Value = makeTypeConstructor("Value__23fc00a2db1374bd3dc1a0ad2d8517ab")(1);
+
+makeModuleEnum(Value)([
+
]);
-// A Value is either:
-// - a literal, without any dependencies.
-// - read from a slot. the Value then has a read-dependency on that slot.
-// - a transformation of another Value, by a function. the function is also a Value.
-const variants = [
- newProduct("literal", () => Dynamic),
- newProduct("read", () => Slot),
- newProduct("transformation", () => prodType(Value, Dynamic)),
-];
-const Value = enumType(variants);
+const _slotType = a => Value => structType([
+ newProduct("value")(_ => Value || _valueType(a)(Slot)),
+ newProduct("rest" )(_ => enumType([
+ newProduct("new")(Slot => structType([
+ newProduct("uuid" )(_ => UUID),
+ newProduct("value")(_ => Value || _valueType(a)(Slot)),
+ ])),
+ newProduct("overwrites")(Slot => structType([
+ newProduct("slot" )(_ => Slot),
+ newProduct("value")(_ => Value || _valueType(a)(Slot)),
+ newProduct("depth")(_ => Int),
+ ])),
+ ])),
+]);
-// console.log(prettyT(Slot));
-// console.log(prettyT(Value));
\ No newline at end of file
+const _valueType = a => Slot => enumType([
+ newProduct("literal" )(_ => a),
+ newProduct("read" )(Value => Slot || _slotType(a)(Value)),
+ newProduct("transform")(Value => structType([
+ newProduct("in" )(_ => Value),
+ newProduct("fn" )(_ => Value),
+ newProduct("out")(_ => a),
+ ])),
+]);
+
+const slotType = makeGeneric(a => _slotType(a)(null));
+const valueType = makeGeneric(a => _valueType(a)(null));
+
+console.log("slotType:", prettyT(slotType));
+console.log("valueType:", prettyT(valueType));
+
+// const valueType = makeGeneric(a => _valueType(a)(_slotType));
\ No newline at end of file
diff --git a/lib/versioning/value.js b/lib/versioning/value.js
index 7e5cc4a..8974776 100644
--- a/lib/versioning/value.js
+++ b/lib/versioning/value.js
@@ -1,4 +1,3 @@
-import { fnType } from "../structures/types.js";
import { deepEqual } from "../util/util.js";
import { inspect } from "node:util";
@@ -20,7 +19,7 @@ export const read = slot => ({
// Value -> Value b> -> Value
export const transform = input => fn => {
const output = fn.out(input.out);
- const _inspect = (depth, options, inspect) => `transform(${inspect(input)}, ${inspect(fn)})`;
+ // const _inspect = (depth, options, inspect) => `transform(${inspect(input)}, ${inspect(fn)})`;
if (input.kind === "literal") {
// optimization: sandwich everything together
return {
@@ -31,7 +30,7 @@ export const transform = input => fn => {
}
else {
return {
- kind: "transformation",
+ kind: "transform",
in: input,
fn,
out: output,
@@ -48,7 +47,7 @@ export const getReadDependencies = value => {
else if (value.kind === "read") {
return new Set([value.slot]);
}
- else if (value.kind === "transformation") {
+ else if (value.kind === "transform") {
return new Set([
...getReadDependencies(value.in),
...getReadDependencies(value.fn),
@@ -80,9 +79,9 @@ export const verifyValue = (value, indent = 0) => {
else if (value.kind === "read") {
compare(value.out, value.slot.value.out, "read");
}
- else if (value.kind === "transformation") {
+ else if (value.kind === "transform") {
compare(value.fn.out(value.in.out),
- value.out, "transformation");
+ value.out, "transform");
success &= verifyValue(value.in, indent + 1);
success &= verifyValue(value.fn, indent + 1);
diff --git a/progress.txt b/progress.txt
index 2e6eb9d..ebeb698 100644
--- a/progress.txt
+++ b/progress.txt
@@ -1,12 +1,20 @@
done:
- - everything is properly typed, up to the meta-circular level
- primitives
- - structures: list, product, sum
+ - structures: list, product, sum, ...
can compose structures, e.g., create list of list of product of sum of ...
- list type is specialized for ListOfByte to use Uint8Array
- - generic types and type inferencing
+ set and dictionary implemented as purely functional red-black tree
+ keys allowed: (anything with total ordering)
+ - primitive values
+ - structural values
+ - dynamically typed values
+ - types
+ - generic types and type unification (inferencing)
+ - recursive types
todo:
+ - dynamically typed values: have 'Any' or 'Top' type?
+
+
- to support sets of slots, need comparison of slots
=> comparison of values
=> problem: how to compare transformed values? their inputs can come from different types
@@ -19,6 +27,5 @@ todo:
(b) dirty: use 'magic' function that compares any JS value
- - typeclass mechanism
+ - interfaces (type classes)
- - what about type links: they connect anything to its type... what is the type of 'anything'?
diff --git a/tests/generics.js b/tests/generics.js
new file mode 100644
index 0000000..97fa238
--- /dev/null
+++ b/tests/generics.js
@@ -0,0 +1,67 @@
+import assert from "node:assert";
+import { assignFn, makeGeneric, unify, UnifyError } from "../lib/generics/generics.js";
+import { getDefaultTypeParser } from "../lib/parser/type_parser.js";
+import { prettyT } from "../lib/util/pretty.js";
+
+const mkType = getDefaultTypeParser();
+
+// It would be better to compare the types directly with 'compareTypes', but the assert-module does not allow passing a custom comparison function.
+
+assert.equal(
+ // actual
+ prettyT(
+ unify(
+ mkType("(a -> Int)"),
+ makeGeneric(() => mkType("[Bool] -> Int")),
+ )
+ ),
+ // expected
+ "([Bool] -> Int)",
+);
+
+assert.equal(
+ // actual
+ prettyT(
+ unify(
+ mkType("(a -> a) -> b"),
+ mkType("(Bool -> Bool) -> a"),
+ )
+ ),
+ // expected
+ "((Bool -> Bool) -> a)",
+);
+
+assert.equal(
+ // actual
+ prettyT(
+ assignFn(
+ mkType("(a -> b) -> [a] -> [b]"),
+ mkType("a -> a")
+ )
+ ),
+ // expected
+ "([a] -> [a])",
+);
+
+assert.equal(
+ // actual
+ prettyT(
+ assignFn(
+ mkType("(a -> Int) -> [a] -> a"),
+ mkType("a -> a")
+ )
+ ),
+ // expected
+ "([Int] -> Int)",
+);
+
+assert.throws(
+ () => {
+ unify(
+ mkType("Int"),
+ mkType("Bool")
+ )
+ },
+ // expected error
+ UnifyError,
+);
diff --git a/tests/parser.js b/tests/parser.js
new file mode 100644
index 0000000..7bfb93d
--- /dev/null
+++ b/tests/parser.js
@@ -0,0 +1,47 @@
+import assert from "node:assert";
+import { getDefaultTypeParser }from "../lib/parser/type_parser.js";
+import { prettyT } from "../lib/util/pretty.js";
+
+const parse = getDefaultTypeParser();
+
+assert.equal(
+ // actual
+ prettyT(parse("Int")),
+ // expected
+ "Int",
+);
+
+assert.equal(
+ // actual
+ prettyT(parse("Int * Bool")),
+ // expected
+ "(Int ⨯ Bool)",
+);
+
+assert.equal(
+ // actual
+ prettyT(parse("(((((((Int)))) => ((Bool)))))")),
+ // expected
+ "(Int => Bool)",
+);
+
+assert.equal(
+ // actual
+ prettyT(parse("#0((Int * #0) + Unit)")),
+ // expected
+ "#0((Int ⨯ #0) + Unit)",
+);
+
+assert.equal(
+ // actual
+ prettyT(parse("#0((a * #0) + Unit")),
+ // expected
+ "#0((a ⨯ #0) + Unit)",
+);
+
+assert.equal(
+ // actual
+ prettyT(parse("(a*b) + (c*d)")),
+ // expected
+ "((a ⨯ b) + (c ⨯ d))",
+);