reorganize directory and file structure
This commit is contained in:
parent
1d826ea8d4
commit
48390b8556
99 changed files with 1155 additions and 1629 deletions
|
|
@ -1,32 +0,0 @@
|
||||||
import { getInst, getType } from "../primitives/dynamic.js";
|
|
||||||
import { SymbolBool, SymbolChar, SymbolDouble, SymbolInt, SymbolType, SymbolUnit } from "../primitives/types.js";
|
|
||||||
import { symbolList, symbolProduct, symbolSet, symbolSum } from "../structures/types.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],
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const makeCompareFn = type => {
|
|
||||||
return type.params.reduce(
|
|
||||||
(acc, cur) => acc(makeCompareFn(cur)),
|
|
||||||
typeSymbolToCmp.get(type.symbol)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const compareDynamic = x => y =>
|
|
||||||
compareTypes(getType(x))(getType(y))
|
|
||||||
|| makeCompareFn(getType(x))(getInst(x))(getInst(y));
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
import { makeCompareFn } from "../compare/registry.js";
|
import { makeCompareFn } from "../lib/compare/registry.js";
|
||||||
import { Int, Unit } from "../primitives/types.js";
|
import { Int, Unit } from "../lib/primitives/primitive_types.js";
|
||||||
import { unit } from "../primitives/unit.js";
|
import { unit } from "../lib/primitives/unit.js";
|
||||||
import { enumType, makeConstructors, makeMatchFn } from "../structures/enum.js";
|
import { makeConstructors, makeMatchFn } from "../lib/structures/enum.js";
|
||||||
import { newProduct } from "../structures/product.js";
|
import { enumType } from "../lib/structures/enum.types.js";
|
||||||
import { lsType, prettyT } from "../structures/types.js";
|
import { newProduct } from "../lib/structures/product.js";
|
||||||
|
import { lsType } from "../lib/structures/type_constructors.js";
|
||||||
|
import { prettyT } from "../lib/util/pretty.js";
|
||||||
|
|
||||||
const variants = [
|
const variants = [
|
||||||
newProduct("price")(Int),
|
newProduct("price")(Int),
|
||||||
newProduct("prices")(lsType(() =>Int)),
|
newProduct("prices")(lsType(_ => Int)),
|
||||||
newProduct("not_found")(Unit),
|
newProduct("not_found")(Unit),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -19,7 +21,7 @@ console.log(" ", prettyT(myEnumType));
|
||||||
const [newPrice, newPrices, newNotFound] = makeConstructors(variants);
|
const [newPrice, newPrices, newNotFound] = makeConstructors(variants);
|
||||||
|
|
||||||
const price = newPrice(10);
|
const price = newPrice(10);
|
||||||
const prices = newPrices({ l: [20, 30] });
|
const prices = newPrices([20, 30]);
|
||||||
const notFound = newNotFound(unit);
|
const notFound = newNotFound(unit);
|
||||||
|
|
||||||
console.log("observe the encoding of different variant instances:");
|
console.log("observe the encoding of different variant instances:");
|
||||||
|
|
@ -29,7 +31,7 @@ console.log(" ", notFound);
|
||||||
|
|
||||||
const myEnumToString = x => makeMatchFn(variants)(x)
|
const myEnumToString = x => makeMatchFn(variants)(x)
|
||||||
(price => `Price: ${price}`)
|
(price => `Price: ${price}`)
|
||||||
(prices => `Prices: ${prices.l}`)
|
(prices => `Prices: ${prices}`)
|
||||||
(() => "Not found!");
|
(() => "Not found!");
|
||||||
|
|
||||||
console.log("observe the generated match function in action:");
|
console.log("observe the generated match function in action:");
|
||||||
|
|
@ -40,9 +42,9 @@ console.log(" ", myEnumToString(notFound));
|
||||||
const compareMyEnum = makeCompareFn(myEnumType);
|
const compareMyEnum = makeCompareFn(myEnumType);
|
||||||
|
|
||||||
console.log("observe the generated compare function in action:");
|
console.log("observe the generated compare function in action:");
|
||||||
console.log(" smaller ->", compareMyEnum(price)(prices));
|
console.log(" should be smaller ->", compareMyEnum(price)(prices));
|
||||||
console.log(" bigger ->", compareMyEnum(prices)(price));
|
console.log(" should be bigger ->", compareMyEnum(prices)(price));
|
||||||
console.log(" bigger ->", compareMyEnum(notFound)(price));
|
console.log(" should be bigger ->", compareMyEnum(notFound)(price));
|
||||||
console.log(" equal ->", compareMyEnum(prices)(prices));
|
console.log(" should be equal ->", compareMyEnum(prices)(prices));
|
||||||
console.log(" smaller ->", compareMyEnum(newPrice(5))(newPrice(6)));
|
console.log(" should be smaller ->", compareMyEnum(newPrice(5))(newPrice(6)));
|
||||||
console.log(" bigger ->", compareMyEnum(newPrices({ l: [5, 6] }))(newPrices({ l: [5, 5] })));
|
console.log(" should be bigger ->", compareMyEnum(newPrices([5, 6]))(newPrices([5, 5])));
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,37 @@
|
||||||
import { Bool, Int } from "../primitives/types.js";
|
import { assign, makeGeneric, unify } from "../lib/generics/generics.js";
|
||||||
import { fnType, lsType } from "../structures/types.js";
|
import { prettyGenT } from "../lib/util/pretty.js";
|
||||||
import { assign, makeGeneric, unify } from "../generics/generics.js";
|
import { getDefaultTypeParser } from "../lib/parser/type_parser.js";
|
||||||
import { prettyGenT, prettyT } from "../util/pretty.js";
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
// a -> Int
|
|
||||||
const a_to_Int = makeGeneric(a => fnType(() => a)(() => Int));
|
|
||||||
console.log((prettyGenT(a_to_Int))); // ∀a: (a -> Int)
|
|
||||||
// Bool -> Int
|
|
||||||
const Bool_to_Int = makeGeneric(() => fnType(() => lsType(() =>Bool))(() => Int));
|
|
||||||
console.log((prettyGenT(Bool_to_Int))); // ∀: ([Bool] -> Int)
|
|
||||||
console.log("should be: [Bool] -> Int")
|
console.log("should be: [Bool] -> Int")
|
||||||
console.log(prettyGenT(unify(a_to_Int, Bool_to_Int)));
|
console.log(prettyGenT(
|
||||||
|
unify(
|
||||||
|
mkType("∀a: (a -> Int)"),
|
||||||
|
makeGeneric(() => mkType("[Bool] -> Int")),
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
// (a -> a) -> b
|
|
||||||
const fnType2 = makeGeneric((a,b) => fnType(() => fnType(a)(a))(() => b));
|
|
||||||
// (Bool -> Bool) -> a
|
|
||||||
const fnType3 = makeGeneric(a => fnType(() => fnType(Bool)(Bool))(() => a));
|
|
||||||
console.log("should be: (Bool -> Bool) -> a");
|
console.log("should be: (Bool -> Bool) -> a");
|
||||||
console.log(prettyT(unify(fnType2, fnType3)));
|
console.log(prettyGenT(
|
||||||
|
unify(
|
||||||
|
mkType("∀a,b: (a -> a) -> b"),
|
||||||
|
mkType("∀a: (Bool -> Bool) -> a"),
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
// (a -> b) -> [a] -> [b]
|
console.log("should be: [a] -> [a]");
|
||||||
const mapFnType = makeGeneric((a,b) =>
|
console.log(prettyGenT(
|
||||||
fnType
|
assign(
|
||||||
(fnType(() => a)(() => b))
|
mkType("∀a,b: (a -> b) -> [a] -> [b]"),
|
||||||
(fnType(() => lsType(() =>a))(() => lsType(() =>b))))
|
mkType("∀a: a -> a")
|
||||||
// a -> a
|
)
|
||||||
const idFnType = makeGeneric((_,__,c) =>
|
));
|
||||||
fnType(() => c)(() => c));
|
|
||||||
console.log("should be: [c] -> [c]");
|
|
||||||
console.log(prettyT(assign(mapFnType, idFnType)));
|
|
||||||
|
|
||||||
// (a -> Int) -> [a] -> a
|
|
||||||
const weirdFnType = makeGeneric(a =>
|
|
||||||
fnType
|
|
||||||
(fnType(() => a)(() => Int))
|
|
||||||
(fnType
|
|
||||||
(lsType(() =>a))
|
|
||||||
(a)))
|
|
||||||
// we call this function with parameter of type (b -> b) ...
|
|
||||||
// giving these substitutions:
|
|
||||||
// a := b
|
|
||||||
// b := Int
|
|
||||||
console.log("should be: [Int] -> Int");
|
console.log("should be: [Int] -> Int");
|
||||||
console.log(prettyT(assign(weirdFnType, idFnType)));
|
console.log(prettyGenT(
|
||||||
|
assign(
|
||||||
|
mkType("∀a: (a -> Int) -> [a] -> a"),
|
||||||
|
mkType("∀a: a -> a")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
import { assign, makeGeneric, unify } from "../generics/generics.js";
|
|
||||||
import { Bool, Int } from "../primitives/types.js";
|
|
||||||
import { newLeft, newRight, match } from "../structures/sum.js";
|
|
||||||
import { fnType, sumType } from "../structures/types.js";
|
|
||||||
import { pretty } from '../util/pretty.js';
|
|
||||||
|
|
||||||
const IntOrBool = sumType(() => Int)(() => Bool);
|
|
||||||
|
|
||||||
|
|
||||||
// console.log(int5);
|
|
||||||
|
|
||||||
console.log(pretty(unify(
|
|
||||||
makeGeneric(() => IntOrBool),
|
|
||||||
makeGeneric(a => sumType(() => Int)(() => a)),
|
|
||||||
)));
|
|
||||||
|
|
||||||
const cipFunction = (x) => {
|
|
||||||
return match(x)({
|
|
||||||
left: x_as_int => (x_as_int === 5),
|
|
||||||
right: x_as_bool => false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const cipFunctionType = fnType
|
|
||||||
(IntOrBool) // in
|
|
||||||
(Bool);
|
|
||||||
|
|
||||||
// console.log(cipFunctionType);
|
|
||||||
// console.log(IntOrBool);
|
|
||||||
|
|
||||||
console.log(assign(
|
|
||||||
makeGeneric(() => cipFunctionType),
|
|
||||||
makeGeneric(() => IntOrBool),
|
|
||||||
));
|
|
||||||
|
|
||||||
console.log("calling newLeft with Int:");
|
|
||||||
const typeAtCallSite = assign(
|
|
||||||
makeGeneric((a, b) =>
|
|
||||||
fnType
|
|
||||||
(a)
|
|
||||||
(sumType(() => a)(() => b))
|
|
||||||
),
|
|
||||||
makeGeneric(() => Int));
|
|
||||||
console.log(pretty(typeAtCallSite));
|
|
||||||
|
|
||||||
|
|
||||||
console.log("calling cipFunction:");
|
|
||||||
console.log(pretty(assign(
|
|
||||||
makeGeneric(() => cipFunctionType),
|
|
||||||
typeAtCallSite,
|
|
||||||
)));
|
|
||||||
|
|
||||||
console.log("valid function calls:");
|
|
||||||
console.log(cipFunction(newLeft(5)));
|
|
||||||
console.log(cipFunction(newLeft(7)));
|
|
||||||
console.log(cipFunction(newRight(true)));
|
|
||||||
|
|
||||||
console.log("invalid function calls:");
|
|
||||||
console.log(cipFunction(5));
|
|
||||||
console.log(cipFunction(newLeft("abc")));
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { assign } from "../generics/generics.js";
|
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
|
||||||
import { Double, Int } from "../primitives/types.js";
|
|
||||||
import { fnType } from "../structures/types.js";
|
|
||||||
import { pretty } from '../util/pretty.js';
|
|
||||||
import { getMul, NumInstances } from "../typeclasses/num.js";
|
|
||||||
import { numDictType } from "./num_type.js";
|
|
||||||
|
|
||||||
const square = numDict => x => getMul(numDict)(x)(x);
|
|
||||||
|
|
||||||
// NumDict a -> a -> a
|
|
||||||
const squareFnType = makeGeneric(a =>
|
|
||||||
fnType
|
|
||||||
(numDictType(a))
|
|
||||||
(fnType(() => a)(() => a))
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log("should be: Int -> Int");
|
|
||||||
console.log(pretty(assign(squareFnType, makeGeneric(() => numDictType(Int)))));
|
|
||||||
|
|
||||||
console.log("should be: Double -> Double");
|
|
||||||
console.log(pretty(assign(squareFnType, makeGeneric(() => numDictType(Double)))));
|
|
||||||
|
|
||||||
// to call 'square' we need:
|
|
||||||
// - the type of our argument (=Int)
|
|
||||||
// - access to a mapping from types to their typeclass instantiation
|
|
||||||
console.log("");
|
|
||||||
console.log(square(NumInstances.get(Int))(42n)); // 1764n
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { parse } from "../parser/parser.js";
|
import { getDefaultTypeParser }from "../lib/parser/type_parser.js";
|
||||||
import { prettyGenT, prettyT } from "../util/pretty.js";
|
import { prettyGenT, prettyT } from "../lib/util/pretty.js";
|
||||||
|
|
||||||
|
const parse = getDefaultTypeParser();
|
||||||
|
|
||||||
console.log(prettyT(parse("Int"))); // Int
|
console.log(prettyT(parse("Int"))); // Int
|
||||||
|
|
||||||
|
|
@ -12,4 +14,3 @@ 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: #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))
|
console.log(prettyGenT(parse("∀a,b,c,d: (a*b) + (c*d)"))); // ∀a,b,c,d: ((a ⨯ b) + (c ⨯ d))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
import { select, number, input } from '@inquirer/prompts';
|
import { select, number, input } from '@inquirer/prompts';
|
||||||
import { ModulePoint } from "../lib/point.js";
|
import { ModulePoint } from "../lib/point.js";
|
||||||
import { DefaultMap } from "../util/defaultmap.js";
|
import { DefaultMap } from "../lib/util/defaultmap.js";
|
||||||
import { pretty } from '../util/pretty.js';
|
import { pretty } from '../lib/util/pretty.js';
|
||||||
import { isFunction } from '../structures/types.js';
|
import { isFunction } from '../structures/types.js';
|
||||||
import { ModuleStd } from '../stdlib.js';
|
import { ModuleStd } from '../lib/stdlib.js';
|
||||||
import { Double, GenericType, Int, SymbolT, Type } from "../primitives/types.js";
|
import { Double, GenericType, Int, SymbolT, Type } from "../primitives/types.js";
|
||||||
import { eqType } from '../primitives/type.js';
|
import { eqType } from '../primitives/type.js';
|
||||||
import { Top } from "../primitives/types.js";
|
import { Top } from "../primitives/types.js";
|
||||||
import { assignFn, makeGeneric, onlyOccurring } from '../generics/generics.js';
|
import { assignFn, makeGeneric, onlyOccurring } from '../lib/generics/generics.js';
|
||||||
import { prettyT } from '../util/pretty.js';
|
import { prettyT } from '../lib/util/pretty.js';
|
||||||
|
import { genUUID } from '../lib/util/random.js';
|
||||||
|
|
||||||
|
|
||||||
// import {emitKeypressEvents} from 'node:readline';
|
// import {emitKeypressEvents} from 'node:readline';
|
||||||
|
|
@ -289,7 +290,7 @@ async function createInstance(t) {
|
||||||
else if (eqType(t)(SymbolT)) {
|
else if (eqType(t)(SymbolT)) {
|
||||||
console.log("Note: you are creating a new Symbol. Even if the description matches that of another symbol (e.g., \"Int\"), a new Symbol will be created that is unique and only equal to itself.");
|
console.log("Note: you are creating a new Symbol. Even if the description matches that of another symbol (e.g., \"Int\"), a new Symbol will be created that is unique and only equal to itself.");
|
||||||
const symbolDescr = await input({message: "enter symbol description:"});
|
const symbolDescr = await input({message: "enter symbol description:"});
|
||||||
return Symbol(symbolDescr);
|
return symbolDescr + '__' + genUUID(16);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("no prompt handler for creating new", prettyT(t));
|
console.log("no prompt handler for creating new", prettyT(t));
|
||||||
|
|
|
||||||
13
examples/rbtree.js
Normal file
13
examples/rbtree.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import createRBTree from "functional-red-black-tree";
|
||||||
|
|
||||||
|
console.log("#############");
|
||||||
|
console.log("## RB Tree ##");
|
||||||
|
console.log("#############");
|
||||||
|
|
||||||
|
// just a small experiment
|
||||||
|
console.log(
|
||||||
|
createRBTree()
|
||||||
|
.insert(1)
|
||||||
|
.insert(1)
|
||||||
|
.insert(2)
|
||||||
|
);
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { compareTypes } from "../compare/type.js";
|
import { compareTypes } from "../lib/compare/type.js";
|
||||||
import { makeGeneric, substitute, unify } from "../generics/generics.js";
|
import { makeGeneric, substitute, unify } from "../lib/generics/generics.js";
|
||||||
import { Double, Int, Unit } from "../primitives/types.js";
|
import { Double, Int, Unit } from "../lib/primitives/primitive_types.js";
|
||||||
import { fnType, lsType, prodType, setType, sumType } from "../structures/types.js";
|
import { fnType, lsType, prodType, sumType, setType } from "../lib/structures/type_constructors.js";
|
||||||
import { prettyGenT, prettyT } from "../util/pretty.js";
|
import { prettyGenT, prettyT } from "../lib/util/pretty.js";
|
||||||
|
|
||||||
|
Error.stackTraceLimit = Infinity;
|
||||||
|
|
||||||
// some recursive types:
|
// some recursive types:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { pretty } from "../util/pretty.js";
|
import { pretty } from "../util/pretty.js";
|
||||||
import { newLiteral, transform, read, getReadDependencies, verifyValue } from "../versioning/value.js";
|
import { newLiteral, transform, read, getReadDependencies, verifyValue } from "../versioning/value.js";
|
||||||
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 { add, emptySet, RBTreeWrapper } from "../structures/set.js";
|
import { add, emptySet, RBTreeWrapper } from "../structures/set.js";
|
||||||
import { compareNumbers } from "../compare/primitives.js";
|
import { compareNumbers } from "../compare/primitives.js";
|
||||||
|
|
||||||
|
|
@ -77,39 +76,20 @@ console.log(pretty({sixSevenEightSlot}));
|
||||||
// console.log("########################");
|
// console.log("########################");
|
||||||
// console.log("## Heterogeneous data ##");
|
// console.log("## Heterogeneous data ##");
|
||||||
// console.log("########################");
|
// console.log("########################");
|
||||||
|
|
||||||
// // 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));
|
||||||
// // Slot<String>
|
// // Slot<String>
|
||||||
// const labelSlot = newSlot(Symbol('label'))(newLiteral("number of sheep"));
|
// const labelSlot = newSlot(Symbol('label'))(newLiteral("number of sheep"));
|
||||||
|
|
||||||
// const combineFn = newLiteral(label => numberOfSheep => `${label}: ${numberOfSheep}`)
|
// const combineFn = newLiteral(label => numberOfSheep => `${label}: ${numberOfSheep}`)
|
||||||
|
|
||||||
// // Slot<String>
|
// // Slot<String>
|
||||||
// const labelAndValueSlotA = overwrite(labelSlot)(
|
// const labelAndValueSlotA = overwrite(labelSlot)(
|
||||||
// transform(read(numberOfSheepSlot))(
|
// transform(read(numberOfSheepSlot))(
|
||||||
// transform(read(labelSlot))(combineFn)));
|
// transform(read(labelSlot))(combineFn)));
|
||||||
|
|
||||||
// const labelAndValueSlotB = overwrite(labelSlot)(
|
// const labelAndValueSlotB = overwrite(labelSlot)(
|
||||||
// transform(read(alternativeNumberOfSheepSlot))(
|
// transform(read(alternativeNumberOfSheepSlot))(
|
||||||
// transform(read(labelSlot))(combineFn)));
|
// transform(read(labelSlot))(combineFn)));
|
||||||
|
|
||||||
// console.log(
|
// console.log(
|
||||||
// add(add(emptySet(compareSlots(compareStrings)))(labelAndValueSlotA))(labelAndValueSlotB)
|
// add(add(emptySet(compareSlots(compareStrings)))(labelAndValueSlotA))(labelAndValueSlotB)
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// merge()(labelSlot)(labelAndValueSlot)
|
// merge()(labelSlot)(labelAndValueSlot)
|
||||||
|
|
||||||
|
|
||||||
console.log("#############")
|
|
||||||
console.log("## RB Tree ##")
|
|
||||||
console.log("#############")
|
|
||||||
|
|
||||||
// just a small experiment
|
|
||||||
console.log(
|
|
||||||
createRBTree()
|
|
||||||
.insert(1)
|
|
||||||
.insert(1)
|
|
||||||
.insert(2)
|
|
||||||
);
|
|
||||||
|
|
|
||||||
23
extra/point/nominal.js
Normal file
23
extra/point/nominal.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
export const cart2polar = ({x, y}) => {
|
||||||
|
const r = Math.sqrt(x*x + y*y);
|
||||||
|
const θ = Math.atan(y/x);
|
||||||
|
return {r, θ};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const polar2cart = ({r, θ}) => {
|
||||||
|
const x = r * Math.cos(θ);
|
||||||
|
const y = r * Math.sin(θ);
|
||||||
|
return {x, y};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const translate = dx => dy => ({x, y}) => {
|
||||||
|
return {left: x+dx, right: y+dy};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const rotate = dθ => ({r, θ}) => {
|
||||||
|
return {r, θ: θ+dθ};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const scale = dr => ({r, θ}) => {
|
||||||
|
return {r: r+dr, θ};
|
||||||
|
}
|
||||||
30
extra/point/nominal.types.js
Normal file
30
extra/point/nominal.types.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { makeTypeParser } from "../../lib/parser/type_parser.js";
|
||||||
|
import { Type } from "../../lib/primitives/primitive_types.js";
|
||||||
|
import { makeTypeConstructor } from "../../lib/meta/type_constructor.js";
|
||||||
|
import { cart2polar, polar2cart, rotate, scale, translate } from "./nominal.js";
|
||||||
|
|
||||||
|
const PointCartesian2D = makeTypeConstructor('PCartesian2D__20bb64ce2cd52cfc6702f7d5d9bd1e60')(0);
|
||||||
|
const PointPolar2D = makeTypeConstructor('PPolar2D__dd566869d57d440e0bc299c53cac3846')(0);
|
||||||
|
|
||||||
|
const examplePoint = {x: 1, y: 2};
|
||||||
|
|
||||||
|
const mkType = makeTypeParser({
|
||||||
|
extraPrimitives: [
|
||||||
|
["PointCartesian2D", PointCartesian2D],
|
||||||
|
["PointPolar2D", PointPolar2D],
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ModulePointNominal = [
|
||||||
|
{i: PointCartesian2D , t: Type},
|
||||||
|
{i: PointPolar2D , t: Type},
|
||||||
|
|
||||||
|
{i: examplePoint , t: PointCartesian2D},
|
||||||
|
|
||||||
|
{i: cart2polar, t: mkType("PointCartesian2D -> PointPolar2D")},
|
||||||
|
{i: polar2cart, t: mkType("PointPolar2D -> PointCartesian2D")},
|
||||||
|
|
||||||
|
{i: translate, t: mkType("Double -> Double -> PointCartesian2D")},
|
||||||
|
{i: rotate , t: mkType("Double -> PointPolar2D -> PointPolar2D")},
|
||||||
|
{i: scale , t: mkType("Double -> PointPolar2D -> PointPolar2D")},
|
||||||
|
];
|
||||||
81
extra/point/structural.types.js
Normal file
81
extra/point/structural.types.js
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { makeTypeParser } from "../../lib/parser/type_parser.js";
|
||||||
|
import { makeModuleStruct } from "../../lib/structures/struct.types.js";
|
||||||
|
import { makeTypeConstructor } from "../../lib/meta/type_constructor.js";
|
||||||
|
import { newProduct } from "../../lib/structures/product.js";
|
||||||
|
import { Double } from "../../lib/primitives/primitive_types.js";
|
||||||
|
|
||||||
|
// Nominal types:
|
||||||
|
const NPoint2DCartesian = makeTypeConstructor('PCartesian2D__7efe2dd14d9b036e2e83d6e4771c88ac')(0);
|
||||||
|
const NPoint2DPolar = makeTypeConstructor('PPolar2D__9297b15478804ee209a91f1af943b67a')(0);
|
||||||
|
|
||||||
|
// Structural types:
|
||||||
|
|
||||||
|
const ModuleCartesian = makeModuleStruct([
|
||||||
|
newProduct("x")(Double),
|
||||||
|
newProduct("y")(Double),
|
||||||
|
]);
|
||||||
|
const ModulePolar = makeModuleStruct([
|
||||||
|
newProduct("r")(Double),
|
||||||
|
newProduct("θ")(Double),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [
|
||||||
|
{i: SPoint2DCartesian},
|
||||||
|
{i: newCartesian},
|
||||||
|
{i: getX},
|
||||||
|
{i: getY},
|
||||||
|
] = ModuleCartesian;
|
||||||
|
|
||||||
|
const [
|
||||||
|
{i: SPoint2DPolar},
|
||||||
|
{i: newPolar},
|
||||||
|
{i: getR},
|
||||||
|
{i: getΘ},
|
||||||
|
] = ModulePolar;
|
||||||
|
|
||||||
|
const cart2polar = cart => {
|
||||||
|
const x = getX(cart);
|
||||||
|
const y = getY(cart);
|
||||||
|
const r = Math.sqrt(x*x + y*y);
|
||||||
|
const θ = Math.atan(y/x);
|
||||||
|
return newPolar(r)(θ);
|
||||||
|
};
|
||||||
|
|
||||||
|
const polar2cart = polar => {
|
||||||
|
const r = getR(polar);
|
||||||
|
const θ = getΘ(polar);
|
||||||
|
const x = r * Math.cos(θ);
|
||||||
|
const y = r * Math.sin(θ);
|
||||||
|
return newCartesian(x)(y);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mkType = makeTypeParser({
|
||||||
|
extraPrimitives: [
|
||||||
|
["NPoint2DCartesian", NPoint2DCartesian],
|
||||||
|
["NPoint2DPolar" , NPoint2DPolar ],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const ModuleConversions = [
|
||||||
|
{ i: NPoint2DCartesian , t: mkType("NPoint2DCartesian") },
|
||||||
|
{ i: NPoint2DPolar , t: mkType("NPoint2DPolar") },
|
||||||
|
{ i: cart2polar , t: mkType("NPoint2DCartesian -> NPoint2DPolar") },
|
||||||
|
{ i: polar2cart , t: mkType("NPoint2DPolar -> NPoint2DCartesian") },
|
||||||
|
];
|
||||||
|
|
||||||
|
const examplePointCart = newCartesian(1)(2);
|
||||||
|
const examplePointPolar = newPolar(0)(5);
|
||||||
|
|
||||||
|
const ModuleExamples = [
|
||||||
|
{ i: examplePointCart , t: SPoint2DCartesian },
|
||||||
|
{ i: examplePointCart , t: NPoint2DCartesian },
|
||||||
|
{ i: examplePointPolar , t: SPoint2DPolar },
|
||||||
|
{ i: examplePointPolar , t: NPoint2DPolar },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ModuleAll = [
|
||||||
|
...ModuleCartesian,
|
||||||
|
...ModulePolar,
|
||||||
|
...ModuleConversions,
|
||||||
|
...ModuleExamples,
|
||||||
|
];
|
||||||
|
|
@ -22,8 +22,7 @@ export const compareBools = x => y => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// The Unit-type has only one instance, which is equal to itself:
|
// The Unit-type has only one instance, which is equal to itself:
|
||||||
export const compareUnits = x => y => 0;
|
export const compareUnits = _ => _ => 0;
|
||||||
|
|
||||||
// Note: dirty assumption that every symbol has unique description.
|
// Symbols are encoded as strings
|
||||||
// This will be fixed once we move from symbols to real UUIDs.
|
export const compareSymbols = a => b => compareStrings(a)(b);
|
||||||
export const compareSymbols = a => b => Number(a !== b) && compareStrings(a.description)(b.description);
|
|
||||||
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));
|
||||||
|
|
@ -28,16 +28,22 @@ export const compareProducts = compareLeft => compareRight => x => y => {
|
||||||
|
|
||||||
// (a -> a -> Int) -> (b -> b -> Int) -> (a | b) -> (a | b) -> Int
|
// (a -> a -> Int) -> (b -> b -> Int) -> (a | b) -> (a | b) -> Int
|
||||||
export const compareSums = compareLeft => compareRight => x => y => {
|
export const compareSums = compareLeft => compareRight => x => y => {
|
||||||
return match(x)(newProduct
|
// console.log("compareSums...", x, y)
|
||||||
(leftValueX => match(y)(newProduct
|
return match(x)
|
||||||
(leftValueY => compareLeft(leftValueX)(leftValueY))
|
(leftValueX => match(y)
|
||||||
((rightValueY) => -1) // x is 'left' and y is 'right' => x < y
|
(leftValueY => compareLeft(leftValueX)(leftValueY)) // both are left
|
||||||
))
|
((_rightValueY) => {
|
||||||
(rightValueX => match(y)(newProduct
|
// console.log("x is 'left' and y is 'right' => x < y")
|
||||||
(leftValueY => 1) // x is 'right' and y is 'left' => x > y
|
return -1;
|
||||||
(rightValueY => compareRight(rightValueX)(rightValueY))
|
}) // 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
|
// (a -> a -> Int) -> {a} -> {a} -> Int
|
||||||
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")},
|
||||||
|
];
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getParams, getSymbol } from "../type_constructor.js";
|
import { getParams, getSymbol } from "../primitives/type.js";
|
||||||
import { compareBools, compareSymbols } from "./primitives.js";
|
import { compareBools, compareNumbers, compareSymbols } from "./primitives.js";
|
||||||
import { compareLists } from "./structures.js";
|
import { compareLists } from "./structures.js";
|
||||||
|
|
||||||
const __compareTypes = state => typeX => typeY => {
|
const __compareTypes = state => typeX => typeY => {
|
||||||
|
|
@ -20,7 +20,7 @@ const __compareTypes = state => typeX => typeY => {
|
||||||
// both sub-types have been visited already in an enclosing call
|
// 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!
|
// 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)
|
// (we cannot compare them, that would result in endless recursion)
|
||||||
return compareSymbols(state.comparing.get(pX))(pY);
|
return compareNumbers(state.comparing.get(pX))(pY);
|
||||||
}
|
}
|
||||||
// none have been visited -> recursively compare
|
// none have been visited -> recursively compare
|
||||||
return __compareTypes(state)(pX)(pY);
|
return __compareTypes(state)(pX)(pY);
|
||||||
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")},
|
||||||
|
];
|
||||||
|
|
@ -96,11 +96,11 @@ export const mergeTwoWay = (m1, m2) => {
|
||||||
// fType, aType: generic types to unify
|
// fType, aType: generic types to unify
|
||||||
// fStack, aStack: internal use.
|
// fStack, aStack: internal use.
|
||||||
const __unify = (typeVars, fType, aType, fStack=[], aStack=[]) => {
|
const __unify = (typeVars, fType, aType, fStack=[], aStack=[]) => {
|
||||||
// console.log("__unify", {typeVars, fType, aType, fStack, aStack});
|
// console.log("__unify", {typeVars, fType: prettyT(fType), aType: prettyT(aType), fStack, aStack});
|
||||||
if (typeVars.has(fType)) {
|
if (typeVars.has(fType)) {
|
||||||
// simplest case: formalType is a type paramater
|
// simplest case: formalType is a type paramater
|
||||||
// => substitute with actualType
|
// => substitute with actualType
|
||||||
// console.log("assign actual to formal");
|
// console.log(`assign ${prettyT(aType)} to ${prettyT(fType)}`);
|
||||||
return {
|
return {
|
||||||
substitutions: new Map([[fType, aType]]),
|
substitutions: new Map([[fType, aType]]),
|
||||||
genericType: {
|
genericType: {
|
||||||
|
|
@ -111,7 +111,7 @@ const __unify = (typeVars, fType, aType, fStack=[], aStack=[]) => {
|
||||||
}
|
}
|
||||||
if (typeVars.has(aType)) {
|
if (typeVars.has(aType)) {
|
||||||
// same as above, but in the other direction
|
// same as above, but in the other direction
|
||||||
// console.log("assign formal to actual");
|
// console.log(`assign ${prettyT(fType)} to ${prettyT(aType)}`);
|
||||||
return {
|
return {
|
||||||
substitutions: new Map([[aType, fType]]),
|
substitutions: new Map([[aType, fType]]),
|
||||||
genericType: {
|
genericType: {
|
||||||
|
|
@ -163,7 +163,7 @@ const __unify = (typeVars, fType, aType, fStack=[], aStack=[]) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const [unifiedSubstitutions, unifiedTypeVars] = unifications.reduce((acc, getParam) => {
|
const [unifiedSubstitutions, unifiedTypeVars] = unifications.reduce((acc, getParam) => {
|
||||||
const self = Symbol();
|
const self = Symbol(); // dirty, just need something unique
|
||||||
const {substitutions, deletions} = mergeTwoWay(acc[0], getParam(self).substitutions);
|
const {substitutions, deletions} = mergeTwoWay(acc[0], getParam(self).substitutions);
|
||||||
return [substitutions, acc[1]
|
return [substitutions, acc[1]
|
||||||
.difference(substitutions)
|
.difference(substitutions)
|
||||||
|
|
@ -206,8 +206,8 @@ export const assign = (genFnType, paramType) => {
|
||||||
let allTypeVars;
|
let allTypeVars;
|
||||||
[allTypeVars, genFnType, paramType] = safeUnionTypeVars(genFnType, paramType);
|
[allTypeVars, genFnType, paramType] = safeUnionTypeVars(genFnType, paramType);
|
||||||
const [inType, outType] = genFnType.type.params;
|
const [inType, outType] = genFnType.type.params;
|
||||||
const {substitutions} = unifyInternal(allTypeVars, inType, paramType.type);
|
const {substitutions} = __unify(allTypeVars, inType(genFnType.type), paramType.type);
|
||||||
const substitutedOutType = substitute(outType, substitutions);
|
const substitutedOutType = substitute(outType(genFnType.type), substitutions);
|
||||||
return recomputeTypeVars(onlyOccurring(substitutedOutType, allTypeVars));
|
return recomputeTypeVars(onlyOccurring(substitutedOutType, allTypeVars));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -215,7 +215,7 @@ export const assignFn = (genFnType, paramType) => {
|
||||||
let allTypeVars;
|
let allTypeVars;
|
||||||
[allTypeVars, genFnType, paramType] = safeUnionTypeVars(genFnType, paramType);
|
[allTypeVars, genFnType, paramType] = safeUnionTypeVars(genFnType, paramType);
|
||||||
const [inType] = genFnType.type.params;
|
const [inType] = genFnType.type.params;
|
||||||
const {substitutions} = unifyInternal(allTypeVars, inType, paramType.type);
|
const {substitutions} = __unify(allTypeVars, inType, paramType.type);
|
||||||
const substitutedFnType = substitute(genFnType.type, substitutions);
|
const substitutedFnType = substitute(genFnType.type, substitutions);
|
||||||
return recomputeTypeVars(onlyOccurring(substitutedFnType, allTypeVars));
|
return recomputeTypeVars(onlyOccurring(substitutedFnType, allTypeVars));
|
||||||
};
|
};
|
||||||
21
lib/meta/type_constructor.js
Normal file
21
lib/meta/type_constructor.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { getHumanReadableName } from "../primitives/symbol.js";
|
||||||
|
|
||||||
|
const __makeTypeConstructor = (symbol, nAry, params) => {
|
||||||
|
if (nAry === 0) {
|
||||||
|
return { symbol, params };
|
||||||
|
}
|
||||||
|
// only for debugging, do we give the function a name
|
||||||
|
const fName = `${getHumanReadableName(symbol).toLowerCase()}Type${params.length>0?params.length:''}`;
|
||||||
|
return {
|
||||||
|
[fName]: typeParam => {
|
||||||
|
if (typeof typeParam !== 'function') {
|
||||||
|
throw new Error("all type params must be functions");
|
||||||
|
}
|
||||||
|
return __makeTypeConstructor(symbol, nAry-1, params.concat([typeParam]));
|
||||||
|
}
|
||||||
|
}[fName];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new nominal type
|
||||||
|
// export const makeTypeConstructor = symbol => nAry => makeTypeConstructorInternal(symbol, nAry);
|
||||||
|
export const makeTypeConstructor = symbol => nAry => __makeTypeConstructor(symbol, nAry, []);
|
||||||
9
lib/meta/type_constructor.types.js
Normal file
9
lib/meta/type_constructor.types.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js"
|
||||||
|
import { makeTypeConstructor } from "./type_constructor.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleTypeConstructor = [
|
||||||
|
// Problem: number of parameters of returned function depends on the 'Int' parameter...
|
||||||
|
// {i: makeTypeConstructor, t: mkType("SymbolT -> Int -> ??")}
|
||||||
|
];
|
||||||
206
lib/parser/type_parser.js
Normal file
206
lib/parser/type_parser.js
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
// A simple, hacked-together recursive parser for types.
|
||||||
|
|
||||||
|
import { Bool, Char, Double, Int, SymbolT, Type, Unit } from "../primitives/primitive_types.js";
|
||||||
|
import { Dynamic } from "../primitives/primitive_types.js";
|
||||||
|
import { dictType, fnType, lsType, prodType, sumType } from "../structures/type_constructors.js";
|
||||||
|
import { setType } from "../structures/type_constructors.js";
|
||||||
|
|
||||||
|
export const makeTypeParser = ({
|
||||||
|
// parser can be extended:
|
||||||
|
extraPrimitives=[],
|
||||||
|
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],
|
||||||
|
['Bool', Bool],
|
||||||
|
['Char', Char],
|
||||||
|
['String', lsType(_ => Char)],
|
||||||
|
['Module', lsType(_ => Dynamic)],
|
||||||
|
['Unit', Unit],
|
||||||
|
['Type', Type],
|
||||||
|
['Dynamic', Dynamic],
|
||||||
|
['SymbolT', SymbolT],
|
||||||
|
['a', a],
|
||||||
|
['b', b],
|
||||||
|
['c', c],
|
||||||
|
['d', d],
|
||||||
|
['e', e],
|
||||||
|
|
||||||
|
...extraPrimitives,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const bracketOperators = new Map([
|
||||||
|
['(', [')', null]],
|
||||||
|
['[', [']', lsType]],
|
||||||
|
['{', ['}', setType]],
|
||||||
|
|
||||||
|
// can only occur at beginning
|
||||||
|
// we use these to extract the type variables
|
||||||
|
['∀', [':', null]],
|
||||||
|
|
||||||
|
...extraBracketOperators,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const infixOperators = new Map([
|
||||||
|
['+', sumType],
|
||||||
|
['|', sumType],
|
||||||
|
['⨯', prodType],
|
||||||
|
['*', prodType],
|
||||||
|
['→', fnType],
|
||||||
|
['->', fnType],
|
||||||
|
['⇒', dictType],
|
||||||
|
['=>', dictType],
|
||||||
|
|
||||||
|
// only used for type variables (e.g., ∀a,b,c:)
|
||||||
|
[',', fnX => fnY => {
|
||||||
|
const x = fnX();
|
||||||
|
const y = fnY();
|
||||||
|
return Array.isArray(x) ? x.concat(y) : [x].concat(y)
|
||||||
|
}],
|
||||||
|
|
||||||
|
...extraInfixOperators,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
const TOKENS = [
|
||||||
|
...bracketOperators.keys(),
|
||||||
|
...[...bracketOperators.values()].map(v => v[0]),
|
||||||
|
...infixOperators.keys(),
|
||||||
|
...primitives.keys(),
|
||||||
|
].toSorted((a,b) => {
|
||||||
|
return (b.length - a.length); // try longer tokens first
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log('TOKENS =', TOKENS);
|
||||||
|
|
||||||
|
const tokenize = expr => {
|
||||||
|
const tokens = [];
|
||||||
|
let i=0;
|
||||||
|
outerloop: while (i<expr.length) {
|
||||||
|
if (/\s/.test(expr[i])) {
|
||||||
|
i++;
|
||||||
|
continue outerloop; // skip whitespace
|
||||||
|
}
|
||||||
|
if (expr[i] === '#') {
|
||||||
|
const label = '#' + parseInt(expr.slice(i+1));
|
||||||
|
tokens.push(label);
|
||||||
|
i += label.length;
|
||||||
|
continue outerloop;
|
||||||
|
}
|
||||||
|
for (const token of TOKENS) {
|
||||||
|
if (expr.startsWith(token, i)) {
|
||||||
|
tokens.push(token);
|
||||||
|
i += token.length;
|
||||||
|
continue outerloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(`Couldn't match any token at position ${i} in\n ${expr}\n ${' '.repeat(i)}^`);
|
||||||
|
}
|
||||||
|
// console.log({tokens});
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
const consumeGroup = (tokens) => {
|
||||||
|
const bracket = bracketOperators.get(tokens[0]);
|
||||||
|
if (bracket === undefined) {
|
||||||
|
// no group, just a single token:
|
||||||
|
const [firstToken, ...rest] = tokens;
|
||||||
|
return [[firstToken], null, rest];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// find where group ends:
|
||||||
|
const [closing, fn] = bracket;
|
||||||
|
const opening = tokens[0]
|
||||||
|
let depth = 1;
|
||||||
|
let i = 1;
|
||||||
|
for (; i<tokens.length; i++) {
|
||||||
|
if (tokens[i] === opening) {
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
else if (tokens[i] === closing) {
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
if (depth === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tokensInGroup = tokens.slice(1, i); // don't include brackets
|
||||||
|
const rest = tokens.slice(i+1);
|
||||||
|
return [tokensInGroup, fn, rest];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseGroup = (tokensInGroup, fn, labels, label) => {
|
||||||
|
// console.log('parseGroup ', tokensInGroup, fn);
|
||||||
|
return (fn === null)
|
||||||
|
? __parse(tokensInGroup, labels, label)
|
||||||
|
: fn(self => {
|
||||||
|
return __parse(tokensInGroup, extendLabels(labels, label, self));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const extendLabels = (labels, label, self) => {
|
||||||
|
return (label === null) ? labels : new Map([...labels, [label, self]])
|
||||||
|
};
|
||||||
|
|
||||||
|
const __parse = (tokens, labels = new Map(), label = null) => {
|
||||||
|
// console.log('parse ', tokens);
|
||||||
|
if (tokens[0].startsWith('#')) {
|
||||||
|
if (labels.has(tokens[0])) {
|
||||||
|
return labels.get(tokens[0]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// pass label and parse 'rest'
|
||||||
|
return __parse(tokens.slice(1), labels, tokens[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tokens.length === 1) {
|
||||||
|
return primitives.get(tokens[0]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const [lhsTokens, fnGrp, rest] = consumeGroup(tokens);
|
||||||
|
if (rest.length === 0) {
|
||||||
|
return parseGroup(lhsTokens, fnGrp, labels, label);
|
||||||
|
}
|
||||||
|
const [operator, ...rhsTokens] = rest;
|
||||||
|
for (const [operatorChar, fn] of infixOperators) {
|
||||||
|
if (operator === operatorChar) {
|
||||||
|
return fn
|
||||||
|
(self => {
|
||||||
|
return parseGroup(lhsTokens, fnGrp, extendLabels(labels, label, self));
|
||||||
|
})(self => {
|
||||||
|
return __parse(rhsTokens, extendLabels(labels, label, self));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("unknown operator: "+operator)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse;
|
||||||
|
}
|
||||||
|
|
||||||
|
let defaultParser;
|
||||||
|
export const getDefaultTypeParser = () => {
|
||||||
|
return defaultParser || (defaultParser = makeTypeParser({}));
|
||||||
|
};
|
||||||
73
lib/point.js
73
lib/point.js
|
|
@ -1,73 +0,0 @@
|
||||||
import { Type } from "../primitives/types.js";
|
|
||||||
import { typedFnType } from "../structures/types.js"
|
|
||||||
import { Double } from "../primitives/types.js";
|
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
// Create nominal types
|
|
||||||
export const PointCartesian2D = makeTypeConstructor(Symbol('PointCartesian2D'))(0);
|
|
||||||
export const PointPolar2D = makeTypeConstructor(Symbol('PointPolar2D'))(0);
|
|
||||||
|
|
||||||
export const cart2polar = ({x, y}) => {
|
|
||||||
const r = Math.sqrt(x*x + y*y);
|
|
||||||
const θ = Math.atan(y/x);
|
|
||||||
return {r, θ};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const polar2cart = ({r, θ}) => {
|
|
||||||
const x = r * Math.cos(θ);
|
|
||||||
const y = r * Math.sin(θ);
|
|
||||||
return {x, y};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const translate = dx => dy => ({x, y}) => {
|
|
||||||
return {left: x+dx, right: y+dy};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const rotate = dθ => ({r, θ}) => {
|
|
||||||
return {r, θ: θ+dθ};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const scale = dr => ({r, θ}) => {
|
|
||||||
return {r: r+dr, θ};
|
|
||||||
}
|
|
||||||
|
|
||||||
const examplePoint = {x: 1, y: 2};
|
|
||||||
|
|
||||||
export const ModulePoint = {l:[
|
|
||||||
{i: -1 , t: Double},
|
|
||||||
{i: 2 , t: Double},
|
|
||||||
{i: examplePoint , t: PointCartesian2D},
|
|
||||||
|
|
||||||
{i: PointCartesian2D , t: Type},
|
|
||||||
{i: PointPolar2D , t: Type},
|
|
||||||
|
|
||||||
...typedFnType(cart2polar, fnType => fnType(() => PointCartesian2D)(() => PointPolar2D)),
|
|
||||||
|
|
||||||
...typedFnType(polar2cart, fnType => fnType(() => PointPolar2D)(() => PointCartesian2D)),
|
|
||||||
|
|
||||||
// Double -> Double -> PointCartesian2D -> PointCartesian2D
|
|
||||||
...typedFnType(translate, fnType =>
|
|
||||||
fnType
|
|
||||||
(Double)
|
|
||||||
(fnType
|
|
||||||
(Double)
|
|
||||||
(fnType
|
|
||||||
(PointCartesian2D)
|
|
||||||
(PointCartesian2D)))),
|
|
||||||
|
|
||||||
// Double -> PointPolar2D -> PointPolar2D
|
|
||||||
...typedFnType(rotate, fnType =>
|
|
||||||
fnType
|
|
||||||
(Double)
|
|
||||||
(fnType
|
|
||||||
(PointPolar2D)
|
|
||||||
(PointPolar2D))),
|
|
||||||
|
|
||||||
// Double -> PointPolar2D -> PointPolar2D
|
|
||||||
...typedFnType(scale, fnType =>
|
|
||||||
fnType
|
|
||||||
(Double)
|
|
||||||
(fnType
|
|
||||||
(PointPolar2D)
|
|
||||||
(PointPolar2D))),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
import { prettyT, typedFnType } from "../structures/types.js"
|
|
||||||
import { Double } from "../primitives/types.js";
|
|
||||||
import { makeConstructor, makeConstructorType, makeGetters, makeGettersTypes, structType } from "../structures/struct.js";
|
|
||||||
import { newProduct } from "../structures/product.js";
|
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
const cartFields = [
|
|
||||||
newProduct("x")(Double),
|
|
||||||
newProduct("y")(Double),
|
|
||||||
];
|
|
||||||
|
|
||||||
const polarFields = [
|
|
||||||
newProduct("r")(Double),
|
|
||||||
newProduct("θ")(Double),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Nominal types:
|
|
||||||
export const NPoint2DCartesian = makeTypeConstructor(Symbol('Point2DCartesian'))(0);
|
|
||||||
export const NPoint2DPolar = makeTypeConstructor(Symbol('Point2DPolar'))(0);
|
|
||||||
|
|
||||||
// Structural types:
|
|
||||||
export const SPoint2DCartesian = structType(cartFields); // (Double, (Double, Unit))
|
|
||||||
export const SPoint2DPolar = structType(polarFields); // (Double, (Double, Unit))
|
|
||||||
|
|
||||||
export const constructorPoint2DCartesian = makeConstructor(cartFields);
|
|
||||||
export const constructorPoint2DPolar = makeConstructor(polarFields);
|
|
||||||
|
|
||||||
export const constructorPoint2DCartesianFnType = makeConstructorType(NPoint2DCartesian)(cartFields);
|
|
||||||
export const constructorPoint2DPolarFnType = makeConstructorType(NPoint2DPolar)(polarFields);
|
|
||||||
|
|
||||||
export const [getX, getY] = makeGetters(cartFields);
|
|
||||||
export const [getR, getΘ] = makeGetters(polarFields);
|
|
||||||
|
|
||||||
export const [getXFnType, getYFnType] = makeGettersTypes(NPoint2DCartesian)(cartFields);
|
|
||||||
export const [getRFnType, getΘFnType] = makeGettersTypes(NPoint2DPolar)(polarFields);
|
|
||||||
|
|
||||||
export const cart2polar = cart => {
|
|
||||||
const x = getX(cart);
|
|
||||||
const y = getY(cart);
|
|
||||||
const r = Math.sqrt(x*x + y*y);
|
|
||||||
const θ = Math.atan(y/x);
|
|
||||||
return constructorPoint2DPolar(r)(θ);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const polar2cart = polar => {
|
|
||||||
const r = getR(polar);
|
|
||||||
const θ = getΘ(polar);
|
|
||||||
const x = r * Math.cos(θ);
|
|
||||||
const y = r * Math.sin(θ);
|
|
||||||
return constructorPoint2DCartesian(x)(y);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cart2Str = cart => {
|
|
||||||
const x = getX(cart);
|
|
||||||
const y = getY(cart);
|
|
||||||
return {l: `{x: ${x}, y: ${y}}`};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const polar2Str = polar => {
|
|
||||||
const r = getR(polar);
|
|
||||||
const θ = getΘ(polar);
|
|
||||||
return {l: `{r: ${r}, θ: ${θ}}`};
|
|
||||||
};
|
|
||||||
|
|
||||||
// export const translate = dx => dy => ({x, y}) => {
|
|
||||||
// return {x: x+dx, y: y+dy};
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export const rotate = dθ => ({r, θ}) => {
|
|
||||||
// return {r, θ: θ+dθ};
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export const scale = dr => ({r, θ}) => {
|
|
||||||
// return {r: r+dr, θ};
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const examplePoint = {x: 1, y: 2};
|
|
||||||
|
|
||||||
const examplePointCart = constructorPoint2DCartesian(1)(2);
|
|
||||||
const examplePointPolar = constructorPoint2DCartesian(0)(5);
|
|
||||||
|
|
||||||
console.log(prettyT(getXFnType));
|
|
||||||
|
|
||||||
export const ModulePoint = {l:[
|
|
||||||
// {i: -1 , t: Double},
|
|
||||||
// {i: 2 , t: Double},
|
|
||||||
// {i: examplePoint , t: Point2DCartesian},
|
|
||||||
|
|
||||||
{i: examplePointCart, t: SPoint2DCartesian},
|
|
||||||
{i: examplePointCart, t: NPoint2DCartesian},
|
|
||||||
|
|
||||||
{i: examplePointPolar, t: SPoint2DPolar},
|
|
||||||
{i: examplePointPolar, t: NPoint2DPolar},
|
|
||||||
|
|
||||||
// {i: SPoint2DCartesian , t: Type},
|
|
||||||
// {i: SPoint2DPolar , t: Type},
|
|
||||||
|
|
||||||
// {i: NPoint2DCartesian , t: Type},
|
|
||||||
// {i: NPoint2DPolar , t: Type},
|
|
||||||
|
|
||||||
{i: getX, t: getXFnType},
|
|
||||||
{i: getY, t: getYFnType},
|
|
||||||
{i: getR, t: getRFnType},
|
|
||||||
{i: getΘ, t: getΘFnType},
|
|
||||||
|
|
||||||
...typedFnType(cart2polar, fnType => fnType(() => NPoint2DCartesian)(() => NPoint2DPolar)),
|
|
||||||
...typedFnType(polar2cart, fnType => fnType(() => NPoint2DPolar)(() => NPoint2DCartesian)),
|
|
||||||
|
|
||||||
// ...typedFnType(polar2cart, fnType => fnType(() => Point2DPolar)(() => Point2DCartesian)),
|
|
||||||
|
|
||||||
// // Double -> Double -> PointCartesian2D -> PointCartesian2D
|
|
||||||
// ...typedFnType(translate, fnType =>
|
|
||||||
// fnType
|
|
||||||
// (Double)
|
|
||||||
// (fnType
|
|
||||||
// (Double)
|
|
||||||
// (fnType
|
|
||||||
// (Point2DCartesian)
|
|
||||||
// (Point2DCartesian)))),
|
|
||||||
|
|
||||||
// ...typedFnType(cart2tuple, fnType => fnType(() => Point2DCartesian)(prodType(() => Double)(() => () => Double))),
|
|
||||||
|
|
||||||
// ...typedFnType(polar2tuple, fnType => fnType(() => Point2DPolar)(prodType(() => Double)(() => () => Double))),
|
|
||||||
|
|
||||||
// // Double -> PointPolar2D -> PointPolar2D
|
|
||||||
// ...typedFnType(rotate, fnType =>
|
|
||||||
// fnType
|
|
||||||
// (Double)
|
|
||||||
// (fnType
|
|
||||||
// (Point2DPolar)
|
|
||||||
// (Point2DPolar))),
|
|
||||||
|
|
||||||
// // Double -> PointPolar2D -> PointPolar2D
|
|
||||||
// ...typedFnType(scale, fnType =>
|
|
||||||
// fnType
|
|
||||||
// (Double)
|
|
||||||
// (fnType
|
|
||||||
// (Point2DPolar)
|
|
||||||
// (Point2DPolar))),
|
|
||||||
]};
|
|
||||||
3
lib/primitives/double.js
Normal file
3
lib/primitives/double.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const addDouble = x => y => x + y;
|
||||||
|
export const mulDouble = x => y => x * y;
|
||||||
|
export const eqDouble = x => y => x === y;
|
||||||
10
lib/primitives/double.types.js
Normal file
10
lib/primitives/double.types.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||||
|
import { addDouble, eqDouble, mulDouble } from "./double.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleDouble = [
|
||||||
|
{ i: addDouble, t: mkType("Double -> Double -> Double") },
|
||||||
|
{ i: mulDouble, t: mkType("Double -> Double -> Double") },
|
||||||
|
{ i: eqDouble, t: mkType("Double -> Double -> Bool") },
|
||||||
|
];
|
||||||
3
lib/primitives/dynamic.js
Normal file
3
lib/primitives/dynamic.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const newDynamic = i => t => ({i, t});
|
||||||
|
export const getInst = lnk => lnk.i;
|
||||||
|
export const getType = lnk => lnk.t;
|
||||||
14
lib/primitives/dynamic.types.js
Normal file
14
lib/primitives/dynamic.types.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||||
|
import { getInst, getType, newDynamic } from "./dynamic.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleDynamic = [
|
||||||
|
{ i: newDynamic, t: mkType("∀a: a -> Type -> Dynamic")},
|
||||||
|
|
||||||
|
// allows us to (unsafely) cast the result to the specific type...
|
||||||
|
// (not sure if this is the right way to go)
|
||||||
|
{ i: getInst, t: mkType("∀a: Dynamic -> a") },
|
||||||
|
|
||||||
|
{ i: getType, t: mkType("Dynamic -> Type") },
|
||||||
|
];
|
||||||
3
lib/primitives/int.js
Normal file
3
lib/primitives/int.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const addInt = x => y => x + y;
|
||||||
|
export const mulInt = x => y => x * y;
|
||||||
|
export const eqInt = x => y => x === y;
|
||||||
10
lib/primitives/int.types.js
Normal file
10
lib/primitives/int.types.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { getDefaultTypeParser }from "../parser/type_parser.js";
|
||||||
|
import { addInt, eqInt, mulInt } from "./int.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleInt = [
|
||||||
|
{ i: addInt, t: mkType("Int -> Int -> Int") },
|
||||||
|
{ i: mulInt, t: mkType("Int -> Int -> Int") },
|
||||||
|
{ i: eqInt, t: mkType("Int -> Int -> Bool") },
|
||||||
|
];
|
||||||
40
lib/primitives/primitive_types.js
Normal file
40
lib/primitives/primitive_types.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
// to break up dependency cycles, primitive types are defined in their own JS module
|
||||||
|
|
||||||
|
import { makeTypeConstructor } from "../meta/type_constructor.js";
|
||||||
|
|
||||||
|
export const SymbolInt = "Int__02a884563f7d480bb14c09be640dfe7a";
|
||||||
|
export const SymbolBool = "Bool__d64c4865bead40439dad62727aaaac2d";
|
||||||
|
export const SymbolDouble = "Double__be70f3c8f53f4419a7866d106faae091";
|
||||||
|
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 SymbolType = "Type__fdbea309d66f49b483b0dd4ceb785f7d";
|
||||||
|
export const SymbolTop = "⊤__38709c3c0039468782103256d4730d1f";
|
||||||
|
export const SymbolGenericType = "GenericType__e9d8010b82f64206afa83d0c163df5d2";
|
||||||
|
export const SymbolDynamic = "Dynamic__3c16c415dba94228ada37dc9d446f54f";
|
||||||
|
|
||||||
|
export const Int = makeTypeConstructor(SymbolInt)(0);
|
||||||
|
export const Bool = makeTypeConstructor(SymbolBool)(0);
|
||||||
|
export const Double = makeTypeConstructor(SymbolDouble)(0);
|
||||||
|
export const Byte = makeTypeConstructor(SymbolByte)(0);
|
||||||
|
export const Char = makeTypeConstructor(SymbolChar)(0);
|
||||||
|
|
||||||
|
// Unit type has only 1 instance, the empty tuple.
|
||||||
|
export const Unit = makeTypeConstructor(SymbolUnit)(0);
|
||||||
|
|
||||||
|
// Bottom type has no instances.
|
||||||
|
export const Bottom = makeTypeConstructor(SymbolBottom)(0);
|
||||||
|
|
||||||
|
export const SymbolT = makeTypeConstructor(SymbolSymbol)(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.
|
||||||
|
|
||||||
|
export const Dynamic = makeTypeConstructor(SymbolDynamic)(0);
|
||||||
31
lib/primitives/primitive_types.types.js
Normal file
31
lib/primitives/primitive_types.types.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
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";
|
||||||
|
|
||||||
|
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 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ModulePrimitiveTypes = [
|
||||||
|
{ i: Int , t: Type },
|
||||||
|
{ i: Bool , t: Type },
|
||||||
|
{ i: Double , t: Type },
|
||||||
|
{ i: Byte , t: Type },
|
||||||
|
{ i: Char , t: Type },
|
||||||
|
{ i: Unit , t: Type },
|
||||||
|
{ i: Bottom , t: Type },
|
||||||
|
{ i: SymbolT , t: Type },
|
||||||
|
{ i: Type , t: Type },
|
||||||
|
{ i: GenericType , t: Type },
|
||||||
|
{ i: Top , t: Type },
|
||||||
|
{ i: Dynamic , t: Type },
|
||||||
|
];
|
||||||
2
lib/primitives/symbol.js
Normal file
2
lib/primitives/symbol.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const getHumanReadableName = symbol => symbol.slice(0, -34); // drop UUID
|
||||||
|
export const eqSymbol = a => b => a === b;
|
||||||
9
lib/primitives/symbol.types.js
Normal file
9
lib/primitives/symbol.types.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js"
|
||||||
|
import { eqSymbol, getHumanReadableName } from "./symbol.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleSymbol = [
|
||||||
|
{ i: getHumanReadableName, t: mkType("SymbolT -> String")},
|
||||||
|
{ i: eqSymbol, t: mkType("SymbolT -> SymbolT -> Bool")},
|
||||||
|
];
|
||||||
6
lib/primitives/type.js
Normal file
6
lib/primitives/type.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { compareTypes } from "../compare/type.js";
|
||||||
|
|
||||||
|
export const getSymbol = type => type.symbol;
|
||||||
|
export const getParams = type => type.params;
|
||||||
|
|
||||||
|
export const eqType = t1 => t2 => compareTypes(t1, t2) === 0;
|
||||||
13
lib/primitives/type.types.js
Normal file
13
lib/primitives/type.types.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// a module is just a set of typed objects
|
||||||
|
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||||
|
import { eqType, getParams, getSymbol } from "./type.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
// each 'typed object' is implicitly an instance of TypeLink (defined below)
|
||||||
|
export const ModuleType = [
|
||||||
|
{i: eqType , t: mkType("Type -> Type -> Bool")},
|
||||||
|
{i: getSymbol, t: mkType("Type -> SymbolT")},
|
||||||
|
{i: getParams, t: mkType("Type -> [Type -> Type]")},
|
||||||
|
];
|
||||||
3
lib/primitives/unit.js
Normal file
3
lib/primitives/unit.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const unit = {};
|
||||||
|
|
||||||
|
export const eqUnit = _ => _ => true;
|
||||||
9
lib/primitives/unit.types.js
Normal file
9
lib/primitives/unit.types.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||||
|
import { eqUnit, unit } from "./unit.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleUnit = [
|
||||||
|
{i: unit , t: mkType("Unit")},
|
||||||
|
{i: eqUnit, t: mkType("Unit -> Unit -> Bool")},
|
||||||
|
];
|
||||||
48
lib/stdlib.js
Normal file
48
lib/stdlib.js
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { ModuleDouble } from "./primitives/double.types.js";
|
||||||
|
import { ModuleDynamic } from "./primitives/dynamic.types.js";
|
||||||
|
import { ModuleInt } from "./primitives/int.types.js";
|
||||||
|
import { ModulePrimitiveSymbols, ModulePrimitiveTypes } from "./primitives/primitive_types.types.js";
|
||||||
|
import { ModuleSymbol } from "./primitives/symbol.types.js";
|
||||||
|
import { ModuleType } from "./primitives/type.types.js";
|
||||||
|
import { ModuleUnit } from "./primitives/unit.types.js";
|
||||||
|
|
||||||
|
import { ModuleDict } from "./structures/dict.types.js"
|
||||||
|
import { ModuleList } from "./structures/list.types.js"
|
||||||
|
import { ModuleProduct } from "./structures/product.types.js"
|
||||||
|
import { ModuleSet } from "./structures/set.types.js"
|
||||||
|
import { ModuleSum } from "./structures/sum.types.js"
|
||||||
|
import { ModuleStructuralSymbols, ModuleTypeConstructors } from "./structures/type_constructors.types.js";
|
||||||
|
|
||||||
|
import { ModuleCompareTypes } from "./compare/type.types.js";
|
||||||
|
import { ModuleComparePrimitives } from "./compare/primitives.types.js";
|
||||||
|
import { ModuleCompareStructures } from "./compare/structures.types.js";
|
||||||
|
|
||||||
|
export const ModuleStd = [
|
||||||
|
// Symbols (for nominal types)
|
||||||
|
...ModulePrimitiveSymbols,
|
||||||
|
...ModuleStructuralSymbols,
|
||||||
|
|
||||||
|
// Nominal types
|
||||||
|
...ModulePrimitiveTypes,
|
||||||
|
...ModuleTypeConstructors,
|
||||||
|
|
||||||
|
// Operations on Primitives
|
||||||
|
...ModuleDouble,
|
||||||
|
...ModuleDynamic,
|
||||||
|
...ModuleInt,
|
||||||
|
...ModuleSymbol,
|
||||||
|
...ModuleType,
|
||||||
|
...ModuleUnit,
|
||||||
|
|
||||||
|
// Operations on Structures
|
||||||
|
...ModuleDict,
|
||||||
|
...ModuleList,
|
||||||
|
...ModuleProduct,
|
||||||
|
...ModuleSet,
|
||||||
|
...ModuleSum,
|
||||||
|
|
||||||
|
// Comparison
|
||||||
|
...ModuleCompareTypes,
|
||||||
|
...ModuleComparePrimitives,
|
||||||
|
...ModuleCompareStructures,
|
||||||
|
];
|
||||||
22
lib/structures/dict.js
Normal file
22
lib/structures/dict.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { RBTreeWrapper } from "../util/rbtree_wrapper.js";
|
||||||
|
import { newProduct } from "./product.js";
|
||||||
|
import { newLeft, newRight } from "./sum.js";
|
||||||
|
|
||||||
|
export const emptyDict = compareFn => RBTreeWrapper.new((x, y) => compareFn(x)(y));
|
||||||
|
|
||||||
|
export const has = dict => key => dict.tree.get(key) === true;
|
||||||
|
export const set = dict => key => value => new RBTreeWrapper(dict.tree.remove(key).insert(key, value));
|
||||||
|
export const remove = dict => key => new RBTreeWrapper(dict.tree.remove(key));
|
||||||
|
export const length = dict => dict.tree.length;
|
||||||
|
|
||||||
|
export const first = dict => dict.tree.begin;
|
||||||
|
export const last = dict => dict.tree.end;
|
||||||
|
|
||||||
|
export const read = iter => {
|
||||||
|
if (iter !== undefined && iter.valid) {
|
||||||
|
return newRight(newProduct(newProduct(iter.key)(iter.value))(iter.clone().next()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return newLeft(unit);
|
||||||
|
}
|
||||||
|
};
|
||||||
20
lib/structures/dict.types.js
Normal file
20
lib/structures/dict.types.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { makeTypeParser } from "../parser/type_parser.js";
|
||||||
|
import { makeTypeConstructor } from "../meta/type_constructor.js";
|
||||||
|
import { emptyDict, first, has, last, length, read, remove, set } from "./dict.js";
|
||||||
|
|
||||||
|
const dictIterator = makeTypeConstructor('DictIterator__d9d175b6bfd1283f00851a99787d0499')(2);
|
||||||
|
|
||||||
|
const mkType = makeTypeParser({
|
||||||
|
extraInfixOperators: [['|=>|', dictIterator]],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ModuleDict = [
|
||||||
|
{ i: emptyDict , t: mkType("∀a,b: (a -> a -> Int) -> (a => b)") },
|
||||||
|
{ i: has , t: mkType("∀a,b: (a => b) -> a -> Bool")},
|
||||||
|
{ i: set , t: mkType("∀a,b: (a => b) -> a -> b -> (a => b)")},
|
||||||
|
{ i: remove , t: mkType("∀a,b: (a => b) -> a -> (a => b)")},
|
||||||
|
{ i: length , t: mkType("∀a,b: (a => b) -> Int")},
|
||||||
|
{ i: first , t: mkType("∀a,b: (a => b) -> (a |=>| b)")},
|
||||||
|
{ i: last , t: mkType("∀a,b: (a => b) -> (a |=>| b)")},
|
||||||
|
{ i: read , t: mkType("∀a,b: (a |=>| b) -> (Unit + ((a*b) * (a |=>| b)))")},
|
||||||
|
];
|
||||||
|
|
@ -1,24 +1,6 @@
|
||||||
import { Bottom } from "../primitives/types.js";
|
|
||||||
import { capitalizeFirstLetter } from "../util/util.js";
|
import { capitalizeFirstLetter } from "../util/util.js";
|
||||||
import { newProduct as newProduct, getLeft, getRight } from "./product.js";
|
import { newProduct as newProduct, getLeft } from "./product.js";
|
||||||
import { newLeft, newRight, match } from "./sum.js";
|
import { newLeft, newRight, match } from "./sum.js";
|
||||||
import { sumType } from "./types.js";
|
|
||||||
|
|
||||||
// 'variants' is an array of (name: string, type: Type) pairs.
|
|
||||||
// e.g., the list of variants:
|
|
||||||
// [ { l: "price" , r: Int },
|
|
||||||
// { l: "prices" , r: [Int] },
|
|
||||||
// { l: "not_found", r: Unit } ]
|
|
||||||
// results in the type:
|
|
||||||
// (Int | ([Int] | (Unit | ⊥)))
|
|
||||||
export const 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));
|
|
||||||
};
|
|
||||||
|
|
||||||
const eatParameters = (numParams, result) => {
|
const eatParameters = (numParams, result) => {
|
||||||
if (numParams === 0) {
|
if (numParams === 0) {
|
||||||
|
|
@ -33,11 +15,9 @@ export const makeMatchFn = variants => {
|
||||||
}
|
}
|
||||||
const [_, ...remainingVariants] = variants;
|
const [_, ...remainingVariants] = variants;
|
||||||
return sum => handler => {
|
return sum => handler => {
|
||||||
return (
|
return match(sum)
|
||||||
match(sum)(newProduct
|
|
||||||
(leftValue => eatParameters(remainingVariants.length, handler(leftValue)))
|
(leftValue => eatParameters(remainingVariants.length, handler(leftValue)))
|
||||||
(rightValue => makeMatchFn(remainingVariants)(rightValue))
|
(rightValue => makeMatchFn(remainingVariants)(rightValue));
|
||||||
));
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
20
lib/structures/enum.types.js
Normal file
20
lib/structures/enum.types.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Bottom } from "../primitives/primitive_types.js";
|
||||||
|
import { getRight } from "./product.js";
|
||||||
|
import { sumType } from "./type_constructors.js";
|
||||||
|
|
||||||
|
// 'variants' is an array of (name: string, type: Type) pairs.
|
||||||
|
// e.g., the list of variants:
|
||||||
|
// [ { l: "price" , r: Int },
|
||||||
|
// { l: "prices" , r: [Int] },
|
||||||
|
// { l: "not_found", r: Unit } ]
|
||||||
|
// results in the type:
|
||||||
|
// (Int | ([Int] | (Unit | ⊥)))
|
||||||
|
|
||||||
|
export const 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));
|
||||||
|
};
|
||||||
16
lib/structures/list.js
Normal file
16
lib/structures/list.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 'normal' implementation
|
||||||
|
export const emptyList = [];
|
||||||
|
// const emptyListType = makeGeneric(a => lsType(() => a));
|
||||||
|
export const get = ls => i => ls[i];
|
||||||
|
export const put = ls => i => elem => ls.with(Number(i), elem);
|
||||||
|
export const push = ls => elem => ls.concat([elem]);
|
||||||
|
export const pop = ls => ls.pop();
|
||||||
|
export const map = ls => fn => ls.map(elem => fn(elem));
|
||||||
|
export const length = ls => ls.length;
|
||||||
|
export const fold = ls => callback => initial => {
|
||||||
|
let acc = initial;
|
||||||
|
for (let i=0; i<ls.length; i++) {
|
||||||
|
acc = callback(acc)(ls[i]);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
15
lib/structures/list.types.js
Normal file
15
lib/structures/list.types.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { getDefaultTypeParser }from "../parser/type_parser.js";
|
||||||
|
import { emptyList, fold, get, length, map, pop, push, put } from "./list.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleList = [
|
||||||
|
{ i: emptyList, t: mkType("∀a: [a]")},
|
||||||
|
{ i: get , t: mkType("∀a: [a] -> Int -> a")},
|
||||||
|
{ i: put , t: mkType("∀a: [a] -> Int -> a -> [a]")},
|
||||||
|
{ i: push , t: mkType("∀a: [a] -> a -> [a]")},
|
||||||
|
{ i: pop , t: mkType("∀a: [a] -> a")},
|
||||||
|
{ i: map , t: mkType("∀a: [a] -> (a -> b) -> [b]")},
|
||||||
|
{ i: length , t: mkType("∀a: [a] -> Int")},
|
||||||
|
{ i: fold , t: mkType("∀a: [a] -> (b -> a -> b) -> b -> b")},
|
||||||
|
];
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// A Product-type always has only two fields, called "left" and "right".
|
// 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.
|
// Product-types of more fields (called Structs) can be constructed by nesting Product-types.
|
||||||
|
|
||||||
|
|
||||||
// In JS, all products are encoded in the same way:
|
// In JS, all products are encoded in the same way:
|
||||||
export const newProduct = l => r => ({l, r});
|
export const newProduct = l => r => ({l, r});
|
||||||
export const getLeft = product => product.l;
|
export const getLeft = product => product.l;
|
||||||
10
lib/structures/product.types.js
Normal file
10
lib/structures/product.types.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||||
|
import { newProduct, getLeft, getRight } from "./product.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleProduct = [
|
||||||
|
{ i: newProduct, t: mkType("∀a,b: a -> b -> (a * b)") },
|
||||||
|
{ i: getLeft , t: mkType("∀a,b: (a * b) -> a" ) },
|
||||||
|
{ i: getRight , t: mkType("∀a,b: (a * b) -> b" ) },
|
||||||
|
];
|
||||||
29
lib/structures/set.js
Normal file
29
lib/structures/set.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { newRight } from "./sum.js";
|
||||||
|
import { newProduct } from "./product.js";
|
||||||
|
import { unit } from "../primitives/unit.js";
|
||||||
|
import { RBTreeWrapper } from "../util/rbtree_wrapper.js";
|
||||||
|
|
||||||
|
// (a -> a -> Int) -> Set(a)
|
||||||
|
export const emptySet = compareFn => RBTreeWrapper.new((x, y) => compareFn(x)(y));
|
||||||
|
|
||||||
|
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 => {
|
||||||
|
if (iter !== undefined && iter.valid) {
|
||||||
|
return newRight(newProduct(iter.key)(iter.clone().next()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return newLeft(unit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const forEach = set => fn => {
|
||||||
|
set.tree.forEach(key => { fn(key); });
|
||||||
|
};
|
||||||
20
lib/structures/set.types.js
Normal file
20
lib/structures/set.types.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
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";
|
||||||
|
|
||||||
|
const setIterator = makeTypeConstructor('SetIterator__f6b0ddd78ed41c58e5a442f2681da011')(1);
|
||||||
|
|
||||||
|
const mkType = makeTypeParser({
|
||||||
|
extraBracketOperators: [['<', ['>', setIterator]]],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ModuleSet = [
|
||||||
|
{ i: emptySet , t: mkType("∀a: (a -> a -> Int) -> {a}") },
|
||||||
|
{ i: has , t: mkType("∀a: {a} -> a -> Bool")},
|
||||||
|
{ i: add , t: mkType("∀a: {a} -> a -> {a}")},
|
||||||
|
{ i: remove , t: mkType("∀a: {a} -> a -> {a}")},
|
||||||
|
{ i: length , t: mkType("∀a: {a} -> Int")},
|
||||||
|
{ i: first , t: mkType("∀a: {a} -> <a>")},
|
||||||
|
{ i: last , t: mkType("∀a: {a} -> <a>")},
|
||||||
|
{ i: read , t: mkType("∀a: <a> -> (Unit + (a * <a>))")},
|
||||||
|
];
|
||||||
33
lib/structures/struct.js
Normal file
33
lib/structures/struct.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { unit } from "../primitives/unit.js";
|
||||||
|
import { capitalizeFirstLetter } from "../util/util.js";
|
||||||
|
import { newProduct, getLeft, getRight } from "./product.js";
|
||||||
|
|
||||||
|
export const makeConstructor = nParams => {
|
||||||
|
const internal = (nParams, ret) => {
|
||||||
|
if (nParams === 0) {
|
||||||
|
const result = ret(unit);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return nextParam => {
|
||||||
|
const wrappedName = 'wrapped_' + ret.name;
|
||||||
|
const newRet = {
|
||||||
|
[wrappedName]: inner => newProduct(nextParam)(ret(inner)),
|
||||||
|
}[wrappedName];
|
||||||
|
return internal(nParams-1, newRet);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const id = x => x;
|
||||||
|
return internal(nParams, id);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeGetters = fieldNames => {
|
||||||
|
if (fieldNames.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const [fieldName, ...rest] = fieldNames;
|
||||||
|
const getterName = `get${capitalizeFirstLetter(fieldName)}`;
|
||||||
|
return [
|
||||||
|
{ [getterName]: obj => getLeft(obj) }[getterName],
|
||||||
|
...makeGetters(rest).map(getter => ({[getter.name]: obj => getter(getRight(obj))}[getter.name])),
|
||||||
|
];
|
||||||
|
};
|
||||||
61
lib/structures/struct.types.js
Normal file
61
lib/structures/struct.types.js
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||||
|
import { newDynamic } from "../primitives/dynamic.js";
|
||||||
|
import { Type, Unit } from "../primitives/primitive_types.js";
|
||||||
|
import { zip } from "../util/util.js";
|
||||||
|
import { map } from "./list.js";
|
||||||
|
import { getLeft, getRight } from "./product.js";
|
||||||
|
import { makeConstructor, makeGetters } from "./struct.js";
|
||||||
|
import { fnType, prodType } from "./type_constructors.js";
|
||||||
|
|
||||||
|
|
||||||
|
// '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 = fieldTypes => {
|
||||||
|
if (fieldTypes.length === 0) {
|
||||||
|
return Unit;
|
||||||
|
}
|
||||||
|
const [fieldType, ...rest] = fieldTypes;
|
||||||
|
return prodType(_ => fieldType)(_ => structType(rest));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeConstructorType = fieldTypes => {
|
||||||
|
if (fieldTypes.length === 0) {
|
||||||
|
return structType(fieldTypes);
|
||||||
|
}
|
||||||
|
const [fieldType, ...rest] = fieldTypes;
|
||||||
|
return fnType(_ => fieldType)(_ => makeConstructorType(rest));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeGettersTypes = fieldTypes => {
|
||||||
|
const type = structType(fieldTypes);
|
||||||
|
return fieldTypes.map(fieldType => {
|
||||||
|
return fnType(_ => type)(_ => fieldType);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const makeModuleStruct = fields => {
|
||||||
|
const fieldNames = map(fields)(getLeft);
|
||||||
|
const fieldTypes = map(fields)(getRight);
|
||||||
|
const type = structType(fieldTypes);
|
||||||
|
const ctor = makeConstructor(fields.length);
|
||||||
|
const ctorType = makeConstructorType(fieldTypes);
|
||||||
|
const getterTypes = makeGettersTypes(fieldTypes);
|
||||||
|
const getters = makeGetters(fieldNames);
|
||||||
|
const module = [
|
||||||
|
{i: type, t: Type},
|
||||||
|
{i: ctor, t: ctorType},
|
||||||
|
...zip(getters, getterTypes)
|
||||||
|
.map(([getter, getterType]) => newDynamic(getter)(getterType)),
|
||||||
|
];
|
||||||
|
return module;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleStruct = [
|
||||||
|
{i: structType, t: mkType("[String*Type] -> Type")},
|
||||||
|
{i: makeModuleStruct, t: mkType("[String*Type] -> [Dynamic]")},
|
||||||
|
];
|
||||||
11
lib/structures/sum.js
Normal file
11
lib/structures/sum.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Sum-type (also called: tagged union, disjoint 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.
|
||||||
|
|
||||||
|
export const newLeft = left => ({t: "L", v: left });
|
||||||
|
export const newRight = right => ({t: "R", v: right});
|
||||||
|
|
||||||
|
export const match = sum => leftHandler => rightHandler =>
|
||||||
|
sum.t === "L"
|
||||||
|
? leftHandler(sum.v)
|
||||||
|
: rightHandler(sum.v);
|
||||||
10
lib/structures/sum.types.js
Normal file
10
lib/structures/sum.types.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { getDefaultTypeParser }from "../parser/type_parser.js";
|
||||||
|
import { match, newLeft, newRight } from "./sum.js";
|
||||||
|
|
||||||
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
export const ModuleSum = [
|
||||||
|
{ i: newLeft , t: mkType("∀a,b: a -> (a + b)") },
|
||||||
|
{ i: newRight , t: mkType("∀a,b: b -> (a + b)") },
|
||||||
|
{ i: match , t: mkType("∀a,b,c: (a + b) -> (a -> c) -> (b -> c) -> c") },
|
||||||
|
];
|
||||||
17
lib/structures/type_constructors.js
Normal file
17
lib/structures/type_constructors.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// to break up dependency cycles, type constructors are defined in their own JS module
|
||||||
|
|
||||||
|
import { makeTypeConstructor } from "../meta/type_constructor.js";
|
||||||
|
|
||||||
|
export const symbolFunction = "Function__c2433e31fa574a2cb3b6b5d62ac9d4b2";
|
||||||
|
export const symbolSum = "Sum__89b731efa6344ea0b6a8663a45cf3ea8";
|
||||||
|
export const symbolProduct = "Product__89351ecdedfb4b05b2a5a6cc0c383e12";
|
||||||
|
export const symbolList = "List__daa8de8a9047435e96034ec64f2da3a1";
|
||||||
|
export const symbolSet = "Set__8fef2c1873df4327ac31bd61d2ecf7e0";
|
||||||
|
export const symbolDict = "Dict__d7158547322549ac9f7f8176aec123dd";
|
||||||
|
|
||||||
|
export const fnType = makeTypeConstructor(symbolFunction)(2);
|
||||||
|
export const sumType = makeTypeConstructor(symbolSum)(2);
|
||||||
|
export const prodType = makeTypeConstructor(symbolProduct)(2);
|
||||||
|
export const lsType = makeTypeConstructor(symbolList)(1);
|
||||||
|
export const setType = makeTypeConstructor(symbolSet)(1);
|
||||||
|
export const dictType = makeTypeConstructor(symbolDict)(2);
|
||||||
23
lib/structures/type_constructors.types.js
Normal file
23
lib/structures/type_constructors.types.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||||
|
import { SymbolT } 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 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ModuleTypeConstructors = [
|
||||||
|
{ i: setType , t: mkType("Type -> Type") },
|
||||||
|
{ i: lsType , t: mkType("Type -> Type") },
|
||||||
|
{ i: prodType , t: mkType("Type -> Type -> Type") },
|
||||||
|
{ i: sumType , t: mkType("Type -> Type -> Type") },
|
||||||
|
{ i: dictType , t: mkType("Type -> Type -> Type") },
|
||||||
|
{ i: fnType , t: mkType("Type -> Type -> Type") },
|
||||||
|
];
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { eqSymbol, getName } from "../primitives/symbol.js";
|
|
||||||
import { Bool, SymbolT, Type } from "../primitives/types.js";
|
|
||||||
import { String } from "../structures/list.js";
|
|
||||||
import { typedFnType } from "../structures/types.js";
|
|
||||||
|
|
||||||
// The way instances of SymbolT are currently encoded, their constructor is not a valid DOPE function, because it is impure.
|
|
||||||
// The only way to construct symbols is to do it in JS code.
|
|
||||||
|
|
||||||
// At some point, we should start encoding SymbolTs as UUIDs rather than JS-Symbols.
|
|
||||||
|
|
||||||
export const ModuleSymbol = {l:[
|
|
||||||
{i: SymbolT, t: Type},
|
|
||||||
|
|
||||||
...typedFnType(getName, fnType =>
|
|
||||||
fnType
|
|
||||||
(SymbolT)
|
|
||||||
(String)
|
|
||||||
),
|
|
||||||
|
|
||||||
...typedFnType(eqSymbol, fnType => fnType(() => SymbolT)(fnType(SymbolT)(() => Bool))),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { Int, SymbolT, Type } from "../primitives/types.js";
|
|
||||||
import { typedFnType } from "../structures/types.js";
|
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
// This function and its type signature cannot be in the same file as 'makeTypeConstructor' because then we get an import cycle among JS modules.
|
|
||||||
export const ModuleTypeConstructor = {l:[
|
|
||||||
...typedFnType(makeTypeConstructor, fnType =>
|
|
||||||
fnType
|
|
||||||
(SymbolT)
|
|
||||||
(fnType
|
|
||||||
(Int)
|
|
||||||
(Type)
|
|
||||||
)),
|
|
||||||
]};
|
|
||||||
18
lib/util/defaultmap.js
Normal file
18
lib/util/defaultmap.js
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
// export class DefaultMap {
|
||||||
|
// constructor(defaultValue, ...rest) {
|
||||||
|
// this.defaultValue = defaultValue;
|
||||||
|
// this.m = new Map(rest);
|
||||||
|
// }
|
||||||
|
// getdefault(key, addToMapping = false) {
|
||||||
|
// return this.m.get(key) || (() => {
|
||||||
|
// const val = this.defaultValue(key);
|
||||||
|
// if (addToMapping)
|
||||||
|
// this.m.set(key, val);
|
||||||
|
// return val;
|
||||||
|
// })();
|
||||||
|
// }
|
||||||
|
// entries() {
|
||||||
|
// return this.m.entries();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
@ -1,17 +1,23 @@
|
||||||
import { inspect } from 'node:util';
|
import { inspect } from 'node:util';
|
||||||
import { symbolDict, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSum } from '../structures/types.js';
|
import { symbolDict, symbolFunction, symbolList, symbolProduct, symbolSum } from '../structures/type_constructors.js';
|
||||||
|
import { symbolSet } from "../structures/type_constructors.js";
|
||||||
import { mapRecursiveStructure } from './util.js';
|
import { mapRecursiveStructure } from './util.js';
|
||||||
|
import { getHumanReadableName } from '../primitives/symbol.js';
|
||||||
|
|
||||||
export function pretty(obj) {
|
export function pretty(obj) {
|
||||||
return inspect(obj, { colors: true, depth: null, breakLength: 120 });
|
return inspect(obj, { colors: true, depth: null, breakLength: 120 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pretty print type
|
// Pretty print Type
|
||||||
export const prettyT = type => {
|
export const prettyT = type => {
|
||||||
return mapRecursiveStructure(([type, m, seen], map) => {
|
return mapRecursiveStructure(([type, m, seen], map) => {
|
||||||
if (typeof type === "symbol") {
|
if (typeof type === "symbol") {
|
||||||
|
// type variable
|
||||||
return type.description;
|
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)) {
|
if (!m.has(type)) {
|
||||||
m.add(type); // next time we encounter this type, we'll only render a placeholder
|
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])());
|
const params = type.params.map(p => map([p(type), m, seen])());
|
||||||
|
|
@ -19,12 +25,10 @@ export const prettyT = type => {
|
||||||
const annot = seen.has(type) ? seen.get(type) : ``;
|
const annot = seen.has(type) ? seen.get(type) : ``;
|
||||||
return renderType(type.symbol, annot, params);
|
return renderType(type.symbol, annot, params);
|
||||||
}
|
}
|
||||||
else {
|
if (!seen.has(type)) {
|
||||||
if (!seen.has(type)) {
|
seen.set(type, `#${seen.size}`);
|
||||||
seen.set(type, `#${seen.size}`);
|
|
||||||
}
|
|
||||||
return seen.get(type);
|
|
||||||
}
|
}
|
||||||
|
return seen.get(type);
|
||||||
})([type, new Set(), new Map()])();
|
})([type, new Set(), new Map()])();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -36,9 +40,10 @@ const renderType = (symbol, annot, params) => {
|
||||||
[symbolSum] : `${annot}(${params[0]} + ${params[1]})`,
|
[symbolSum] : `${annot}(${params[0]} + ${params[1]})`,
|
||||||
[symbolProduct] : `${annot}(${params[0]} ⨯ ${params[1]})`,
|
[symbolProduct] : `${annot}(${params[0]} ⨯ ${params[1]})`,
|
||||||
[symbolDict] : `${annot}(${params[0]} => ${params[1]})`,
|
[symbolDict] : `${annot}(${params[0]} => ${params[1]})`,
|
||||||
}[symbol] || symbol.description;
|
}[symbol] || getHumanReadableName(symbol);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Pretty print GenericType
|
||||||
export const prettyGenT = genericType => {
|
export const prettyGenT = genericType => {
|
||||||
return `∀${[...genericType.typeVars].map(prettyT).sort((a, b) => a.localeCompare(b)).join(",")}: ${prettyT(genericType.type)}`;
|
return `∀${[...genericType.typeVars].map(prettyT).sort((a, b) => a.localeCompare(b)).join(",")}: ${prettyT(genericType.type)}`;
|
||||||
};
|
};
|
||||||
16
lib/util/random.js
Normal file
16
lib/util/random.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// IMPURE
|
||||||
|
export const genUUID = (len=16) => {
|
||||||
|
const arr = crypto.getRandomValues(new Uint8Array(len));
|
||||||
|
let result = "";
|
||||||
|
for (let i=0; i<len; i++) {
|
||||||
|
const unsignedByte = arr[i];
|
||||||
|
if (unsignedByte < 16) {
|
||||||
|
result += '0' + unsignedByte.toString(16);
|
||||||
|
} else {
|
||||||
|
result += unsignedByte.toString(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(genUUID())
|
||||||
21
lib/util/rbtree_wrapper.js
Normal file
21
lib/util/rbtree_wrapper.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Tiny wrapper around function-red-black-tree that overrides the [inspect.custom] symbol so when we print it (during debugging) we just see the (key=>value)-pairs instead of the tree structure.
|
||||||
|
|
||||||
|
import createRBTree from "functional-red-black-tree";
|
||||||
|
import { inspect } from "util";
|
||||||
|
|
||||||
|
export class RBTreeWrapper {
|
||||||
|
constructor(tree) {
|
||||||
|
this.tree = tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
static new(compareFn) {
|
||||||
|
return new RBTreeWrapper(createRBTree(compareFn))
|
||||||
|
}
|
||||||
|
|
||||||
|
// pretty print to console
|
||||||
|
[inspect.custom](depth, options, inspect) {
|
||||||
|
const entries = [];
|
||||||
|
this.tree.forEach((key, val) => { entries.push(`${inspect(key)} => ${inspect(val)}`); });
|
||||||
|
return `RBTree(${this.tree.length}) {${entries.join(', ')}}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
27
lib/util/util.js
Normal file
27
lib/util/util.js
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// zip two arrays
|
||||||
|
export function zip(a, b) {
|
||||||
|
return a.map((k, i) => [k, b[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function capitalizeFirstLetter(val) {
|
||||||
|
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const _mapRecursiveStructure = mapping => transform => root => {
|
||||||
|
const found = mapping.get(root);
|
||||||
|
if (found) {
|
||||||
|
// already mapped
|
||||||
|
// return existing result to prevent endless recursion
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
// note the indirection (wrapped in lamda), this allows the user to recursively map the children (which may refer to the root) without yet having finished mapping the root.
|
||||||
|
let memo;
|
||||||
|
const result = () => {
|
||||||
|
// memoization is necessary for correctness
|
||||||
|
return memo || (memo = transform(root, _mapRecursiveStructure(mapping)(transform)));
|
||||||
|
};
|
||||||
|
mapping.set(root, result);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mapRecursiveStructure = _mapRecursiveStructure(new Map());
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
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";
|
import { compareSlots } from "../compare/versioning.js";
|
||||||
174
parser/parser.js
174
parser/parser.js
|
|
@ -1,174 +0,0 @@
|
||||||
import { Bool, Char, Double, Int, Unit } from "../primitives/types.js";
|
|
||||||
import { dictType, fnType, lsType, prodType, setType, sumType } from "../structures/types.js";
|
|
||||||
|
|
||||||
const bracketOperators = new Map([
|
|
||||||
['(', [')', null]],
|
|
||||||
['[', [']', lsType]],
|
|
||||||
['{', ['}', setType]],
|
|
||||||
|
|
||||||
// can only occur at beginning
|
|
||||||
// we use these to extract the type variables
|
|
||||||
['∀', [':', null]],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const infixOperators = new Map([
|
|
||||||
['+', sumType],
|
|
||||||
['|', sumType],
|
|
||||||
['⨯', prodType],
|
|
||||||
['*', prodType],
|
|
||||||
['→', fnType],
|
|
||||||
['->', fnType],
|
|
||||||
['⇒', dictType],
|
|
||||||
['=>', dictType],
|
|
||||||
|
|
||||||
// only used for type variables (e.g., ∀a,b,c:)
|
|
||||||
[',', fnX => fnY => {
|
|
||||||
const x = fnX();
|
|
||||||
const y = fnY();
|
|
||||||
return Array.isArray(x) ? x.concat(y) : [x].concat(y)
|
|
||||||
}],
|
|
||||||
]);
|
|
||||||
|
|
||||||
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],
|
|
||||||
['Bool', Bool],
|
|
||||||
['Char', Char],
|
|
||||||
['Unit', Unit],
|
|
||||||
['a', a],
|
|
||||||
['b', b],
|
|
||||||
['c', c],
|
|
||||||
['d', d],
|
|
||||||
['e', e],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const TOKENS = [
|
|
||||||
...bracketOperators.keys(),
|
|
||||||
...[...bracketOperators.values()].map(v => v[0]),
|
|
||||||
...infixOperators.keys(),
|
|
||||||
...primitives.keys(),
|
|
||||||
];
|
|
||||||
|
|
||||||
// console.log('TOKENS =', TOKENS);
|
|
||||||
|
|
||||||
const tokenize = expr => {
|
|
||||||
const tokens = [];
|
|
||||||
let i=0;
|
|
||||||
outerloop: while (i<expr.length) {
|
|
||||||
if (/\s/.test(expr[i])) {
|
|
||||||
i++;
|
|
||||||
continue outerloop; // skip whitespace
|
|
||||||
}
|
|
||||||
if (expr[i] === '#') {
|
|
||||||
const label = '#' + parseInt(expr.slice(i+1));
|
|
||||||
tokens.push(label);
|
|
||||||
i += label.length;
|
|
||||||
continue outerloop;
|
|
||||||
}
|
|
||||||
for (const token of TOKENS) {
|
|
||||||
if (expr.startsWith(token, i)) {
|
|
||||||
tokens.push(token);
|
|
||||||
i += token.length;
|
|
||||||
continue outerloop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(`Couldn't match any token at position ${i} in\n ${expr}\n ${' '.repeat(i)}^`);
|
|
||||||
}
|
|
||||||
// console.log({tokens});
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
const consumeGroup = (tokens) => {
|
|
||||||
const bracket = bracketOperators.get(tokens[0]);
|
|
||||||
if (bracket === undefined) {
|
|
||||||
// no group, just a single token:
|
|
||||||
const [firstToken, ...rest] = tokens;
|
|
||||||
return [[firstToken], null, rest];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// find where group ends:
|
|
||||||
const [closing, fn] = bracket;
|
|
||||||
const opening = tokens[0]
|
|
||||||
let depth = 1;
|
|
||||||
let i = 1;
|
|
||||||
for (; i<tokens.length; i++) {
|
|
||||||
if (tokens[i] === opening) {
|
|
||||||
depth++;
|
|
||||||
}
|
|
||||||
else if (tokens[i] === closing) {
|
|
||||||
depth--;
|
|
||||||
}
|
|
||||||
if (depth === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const tokensInGroup = tokens.slice(1, i); // don't include brackets
|
|
||||||
const rest = tokens.slice(i+1);
|
|
||||||
return [tokensInGroup, fn, rest];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseGroup = (tokensInGroup, fn, labels, label) => {
|
|
||||||
// console.log('parseGroup ', tokensInGroup, fn);
|
|
||||||
return (fn === null)
|
|
||||||
? __parse(tokensInGroup, labels, label)
|
|
||||||
: fn(self => {
|
|
||||||
return __parse(tokensInGroup, extendLabels(labels, label, self));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const extendLabels = (labels, label, self) => {
|
|
||||||
return (label === null) ? labels : new Map([...labels, [label, self]])
|
|
||||||
};
|
|
||||||
|
|
||||||
const __parse = (tokens, labels = new Map(), label = null) => {
|
|
||||||
// console.log('parse ', tokens);
|
|
||||||
if (tokens[0].startsWith('#')) {
|
|
||||||
if (labels.has(tokens[0])) {
|
|
||||||
return labels.get(tokens[0]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// pass label and parse 'rest'
|
|
||||||
return __parse(tokens.slice(1), labels, tokens[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tokens.length === 1) {
|
|
||||||
return primitives.get(tokens[0]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const [lhsTokens, fnGrp, rest] = consumeGroup(tokens);
|
|
||||||
if (rest.length === 0) {
|
|
||||||
return parseGroup(lhsTokens, fnGrp, labels, label);
|
|
||||||
}
|
|
||||||
const [operator, ...rhsTokens] = rest;
|
|
||||||
for (const [operatorChar, fn] of infixOperators) {
|
|
||||||
if (operator === operatorChar) {
|
|
||||||
return fn
|
|
||||||
(self => {
|
|
||||||
return parseGroup(lhsTokens, fnGrp, extendLabels(labels, label, self));
|
|
||||||
})(self => {
|
|
||||||
return __parse(rhsTokens, extendLabels(labels, label, self));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error("unknown operator: "+operator)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export 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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import { fnType, typedFnType } from "../structures/types.js";
|
|
||||||
import { Type } from "./types.js";
|
|
||||||
import { Bool } from "./types.js";
|
|
||||||
|
|
||||||
const eqBool = x => y => x === y;
|
|
||||||
|
|
||||||
export const ModuleBool = {l:[
|
|
||||||
{i: true , t: Bool},
|
|
||||||
{i: false, t: Bool},
|
|
||||||
|
|
||||||
{i: Bool , t: Type},
|
|
||||||
|
|
||||||
// Bool -> Bool -> Bool
|
|
||||||
...typedFnType(eqBool, fnType => fnType(() => Bool)(fnType(Bool)(() => Bool))),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
|
||||||
import { Type } from "./types.js";
|
|
||||||
import {Byte, Bool} from "./types.js";
|
|
||||||
|
|
||||||
const eqByte = x => y => x === y;
|
|
||||||
|
|
||||||
export const ModuleByte = {l:[
|
|
||||||
{i: Byte , t: Type },
|
|
||||||
|
|
||||||
...typedFnType(eqByte, fnType => fnType(() => Byte)(fnType(Byte)(() => Bool))),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
|
||||||
import { Type } from "./types.js";
|
|
||||||
import {Char, Bool} from "./types.js";
|
|
||||||
|
|
||||||
const eq = x => y => x === y;
|
|
||||||
|
|
||||||
export const ModuleChar = {l:[
|
|
||||||
{i: Char, t: Type},
|
|
||||||
|
|
||||||
...typedFnType(eq, fnType =>
|
|
||||||
fnType
|
|
||||||
(Char)
|
|
||||||
(fnType
|
|
||||||
(Char)
|
|
||||||
(Bool))),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
|
||||||
import { Type } from "./types.js";
|
|
||||||
import {Bool, Double} from "./types.js";
|
|
||||||
|
|
||||||
export const addDouble = x => y => x + y;
|
|
||||||
export const mulDouble = x => y => x * y;
|
|
||||||
export const eqDouble = x => y => x === y;
|
|
||||||
|
|
||||||
export const ModuleDouble = {l:[
|
|
||||||
{i: Double, t: Type},
|
|
||||||
|
|
||||||
...typedFnType(addDouble, fnType => fnType(() => Double)(fnType(Double)(() => Double))),
|
|
||||||
...typedFnType(mulDouble, fnType => fnType(() => Double)(fnType(Double)(() => Double))),
|
|
||||||
...typedFnType(eqDouble, fnType => fnType(() => Double)(fnType(Double)(() => Bool))),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
|
||||||
import { Top, Type } from "./types.js";
|
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
// A type-link, connecting a value to its Type.
|
|
||||||
export const symbolDynamic = Symbol('Dynamic');
|
|
||||||
export const Dynamic = makeTypeConstructor(symbolDynamic)(0);
|
|
||||||
|
|
||||||
export const getInst = lnk => lnk.i;
|
|
||||||
export const getType = lnk => lnk.t;
|
|
||||||
|
|
||||||
export const ModuleDynamic = {l:[
|
|
||||||
{i: Dynamic, t: Type},
|
|
||||||
{i: Top , t: Type},
|
|
||||||
|
|
||||||
...typedFnType(getInst, fnType => fnType(() => Dynamic)(() => Top)),
|
|
||||||
...typedFnType(getType, fnType => fnType(() => Dynamic)(() => Top)),
|
|
||||||
]};
|
|
||||||
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import { newLeft, newRight } from "../structures/sum.js";
|
|
||||||
import { setType, sumType, typedFnType } from "../structures/types.js";
|
|
||||||
import { Top, GenericType, SymbolT, Type, Unit } from "./types.js";
|
|
||||||
import { unit } from "./unit.js";
|
|
||||||
|
|
||||||
export const getType = genericType => genericType.type;
|
|
||||||
export const getTypeVars = genericType => genericType.typeVars;
|
|
||||||
|
|
||||||
export const toNonGeneric = genericType => (genericType.typeVars.size === 0)
|
|
||||||
? newRight(genericType.type)
|
|
||||||
: newLeft(unit);
|
|
||||||
|
|
||||||
export const ModuleGenericType = {l:[
|
|
||||||
{i: GenericType, t: Top},
|
|
||||||
|
|
||||||
// ...typedFnType(getType, fnType => fnType(() => GenericType)(() => Type)),
|
|
||||||
|
|
||||||
// ...typedFnType(getTypeVars, fnType => fnType(() => GenericType)(() => set(() => SymbolT))),
|
|
||||||
|
|
||||||
...typedFnType(toNonGeneric, fnType => fnType(() => GenericType)(sumType(() => Unit)(() => () => Type))),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
|
||||||
import { Type } from "./types.js";
|
|
||||||
|
|
||||||
import {Bool, Int} from "./types.js";
|
|
||||||
|
|
||||||
export const addInt = x => y => x + y;
|
|
||||||
export const mulInt = x => y => x * y;
|
|
||||||
export const eqInt = x => y => x === y;
|
|
||||||
|
|
||||||
const serialize = x => x.toString();
|
|
||||||
const deserialize = str => BigInt(str);
|
|
||||||
|
|
||||||
export const ModuleInt = {l:[
|
|
||||||
{i: Int , t: Type },
|
|
||||||
|
|
||||||
...typedFnType(addInt, fnType => fnType(() => Int)(fnType(Int)(() => Int))),
|
|
||||||
...typedFnType(mulInt, fnType => fnType(() => Int)(fnType(Int)(() => Int))),
|
|
||||||
...typedFnType(eqInt , fnType => fnType(() => Int)(fnType(Int)(() => Bool))),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
// The functions are only defined here. For their types, see lib/symbol.js
|
|
||||||
|
|
||||||
// Cannot turn the constructor into a DOPE function, because it is NOT PURE:
|
|
||||||
// export const constructSymbol = name => Symbol(name);
|
|
||||||
|
|
||||||
export const getName = symbol => symbol.description;
|
|
||||||
|
|
||||||
export const eqSymbol = a => b => a === b;
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { Bool, SymbolT, Type } from "./types.js";
|
|
||||||
import { isFunction, lsType, typedFnType } from "../structures/types.js";
|
|
||||||
import { getSymbol, getParams } from "../type_constructor.js";
|
|
||||||
import { deepEqual } from "../util/util.js";
|
|
||||||
|
|
||||||
// we can test whether types are equal:
|
|
||||||
export const eqType = t1 => t2 => deepEqual(t1, t2);
|
|
||||||
|
|
||||||
// a module is just a set of typed objects
|
|
||||||
// each 'typed object' is implicitly an instance of TypeLink (defined below)
|
|
||||||
export const ModuleType = {l:[
|
|
||||||
// TODO? maybe follow Lean so
|
|
||||||
// Type.{0} : Type.{1}
|
|
||||||
// Type.{1} : Type.{2}
|
|
||||||
// ...
|
|
||||||
// see: https://lean-lang.org/functional_programming_in_lean/functor-applicative-monad/universes.html
|
|
||||||
|
|
||||||
{i: Type, t: Type},
|
|
||||||
|
|
||||||
// Type -> Type -> Bool
|
|
||||||
...typedFnType(eqType, fnType =>
|
|
||||||
fnType
|
|
||||||
(Type)
|
|
||||||
(fnType
|
|
||||||
(Type)
|
|
||||||
(Bool)
|
|
||||||
)),
|
|
||||||
|
|
||||||
...typedFnType(getSymbol, fnType => fnType(() => Type)(() => SymbolT)),
|
|
||||||
...typedFnType(getParams, fnType => fnType(() => Type)(() => lsType(() =>Type))),
|
|
||||||
|
|
||||||
...typedFnType(isFunction, fnType => fnType(() => Type)(() => Bool)),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
// to break up dependency cycles, primitive types are defined in their own JS module
|
|
||||||
|
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
export const SymbolInt = Symbol('Int');
|
|
||||||
export const SymbolBool = Symbol('Bool');
|
|
||||||
export const SymbolDouble = Symbol('Double');
|
|
||||||
export const SymbolByte = Symbol('Byte');
|
|
||||||
export const SymbolChar = Symbol('Char');
|
|
||||||
export const SymbolUnit = Symbol('Unit');
|
|
||||||
export const SymbolBottom = Symbol('⊥');
|
|
||||||
export const SymbolSymbol = Symbol('Symbol');
|
|
||||||
export const SymbolType = Symbol('Type');
|
|
||||||
export const symbolTop = Symbol('⊤');
|
|
||||||
export const SymbolGenericType = Symbol('GenericType');
|
|
||||||
|
|
||||||
export const Int = makeTypeConstructor(SymbolInt)(0);
|
|
||||||
export const Bool = makeTypeConstructor(SymbolBool)(0);
|
|
||||||
export const Double = makeTypeConstructor(SymbolDouble)(0);
|
|
||||||
export const Byte = makeTypeConstructor(SymbolByte)(0);
|
|
||||||
export const Char = makeTypeConstructor(SymbolChar)(0);
|
|
||||||
|
|
||||||
// Unit type has only 1 instance, the empty tuple.
|
|
||||||
export const Unit = makeTypeConstructor(SymbolUnit)(0);
|
|
||||||
|
|
||||||
// Bottom type has no instances.
|
|
||||||
export const Bottom = makeTypeConstructor(SymbolBottom)(0);
|
|
||||||
|
|
||||||
export const SymbolT = makeTypeConstructor(SymbolSymbol)(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);
|
|
||||||
|
|
||||||
export const ModuleSymbols = {l:[
|
|
||||||
{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},
|
|
||||||
]};
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
|
||||||
import { Bool, Type, Unit } from "./types.js";
|
|
||||||
|
|
||||||
export const eqUnit = x => y => x === y;
|
|
||||||
|
|
||||||
export const unit = {};
|
|
||||||
|
|
||||||
export const ModuleUnit = {l:[
|
|
||||||
{i: unit, t: Unit},
|
|
||||||
|
|
||||||
{i: Unit, t: Type},
|
|
||||||
|
|
||||||
// Unit -> Unit -> Bool
|
|
||||||
...typedFnType(eqUnit, fnType => fnType(() => Unit)(fnType(Unit)(() => Bool))),
|
|
||||||
]};
|
|
||||||
42
stdlib.js
42
stdlib.js
|
|
@ -1,42 +0,0 @@
|
||||||
import { ModuleSymbol } from "./lib/symbol.js";
|
|
||||||
import { ModuleTypeConstructor } from "./lib/type_constructor.js";
|
|
||||||
import { ModuleBool } from "./primitives/bool.js";
|
|
||||||
import { ModuleByte } from "./primitives/byte.js";
|
|
||||||
import { ModuleChar } from "./primitives/char.js";
|
|
||||||
import { ModuleDouble } from "./primitives/double.js";
|
|
||||||
import { ModuleInt } from "./primitives/int.js";
|
|
||||||
import { ModuleSymbols } from "./primitives/types.js";
|
|
||||||
import { ModuleUnit } from "./primitives/unit.js";
|
|
||||||
import { ModuleFunction } from "./structures/function.js";
|
|
||||||
import { ModuleList } from "./structures/list.js";
|
|
||||||
import { ModuleProduct } from "./structures/product.js";
|
|
||||||
import { ModuleSum } from "./structures/sum.js";
|
|
||||||
import { ModuleType } from "./primitives/type.js";
|
|
||||||
import { ModuleDynamic } from "./primitives/dynamic.js";
|
|
||||||
import { ModuleSet } from "./structures/set.js";
|
|
||||||
import { ModuleGenericType } from "./primitives/generic_type.js";
|
|
||||||
|
|
||||||
export const ModuleStd = {l:[
|
|
||||||
...ModuleType.l,
|
|
||||||
...ModuleGenericType.l,
|
|
||||||
...ModuleDynamic.l,
|
|
||||||
|
|
||||||
...ModuleTypeConstructor.l,
|
|
||||||
...ModuleSymbols.l,
|
|
||||||
|
|
||||||
// Primitive types
|
|
||||||
...ModuleBool.l,
|
|
||||||
...ModuleByte.l,
|
|
||||||
...ModuleChar.l,
|
|
||||||
...ModuleDouble.l,
|
|
||||||
...ModuleInt.l,
|
|
||||||
...ModuleSymbol.l,
|
|
||||||
...ModuleUnit.l,
|
|
||||||
|
|
||||||
// Types that consist of other types
|
|
||||||
...ModuleFunction.l,
|
|
||||||
...ModuleList.l,
|
|
||||||
...ModuleProduct.l,
|
|
||||||
...ModuleSum.l,
|
|
||||||
...ModuleSet.l,
|
|
||||||
]};
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { Type } from "../primitives/types.js";
|
|
||||||
import { typedFnType } from "./types.js";
|
|
||||||
import { fnType } from "./types.js";
|
|
||||||
|
|
||||||
export const ModuleFunction = {l:[
|
|
||||||
// binary type constructor: Type -> Type -> Type
|
|
||||||
...typedFnType(fnType, fnType => fnType
|
|
||||||
/* in */ (Type)
|
|
||||||
/* out */ (fnType
|
|
||||||
/* in */ (Type)
|
|
||||||
/* out */ (Type)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
import { typedFnType } from "./types.js";
|
|
||||||
import { Char, GenericType, Type } from "../primitives/types.js";
|
|
||||||
import { Int } from "../primitives/types.js";
|
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
|
||||||
import { lsType } from "./types.js";
|
|
||||||
import { Dynamic } from "../primitives/dynamic.js"
|
|
||||||
|
|
||||||
// 'normal' implementation
|
|
||||||
export const emptyList = {l:[]};
|
|
||||||
// const emptyListType = makeGeneric(a => lsType(() => a));
|
|
||||||
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(() =>Dynamic);
|
|
||||||
|
|
||||||
export const ModuleList = {l:[
|
|
||||||
// // Type -> Type
|
|
||||||
// ...typedFnType(lsType, fnType =>
|
|
||||||
// fnType
|
|
||||||
// /* in */ (Type)
|
|
||||||
// /* out */ (Type)
|
|
||||||
// ),
|
|
||||||
|
|
||||||
// // [a]
|
|
||||||
// // {i: emptyList, t: emptyListType},
|
|
||||||
// // {i: emptyListType, t: GenericType},
|
|
||||||
|
|
||||||
// // [a] -> Int -> a
|
|
||||||
// ...typedFnType(get, fnType =>
|
|
||||||
// makeGeneric(a =>
|
|
||||||
// fnType
|
|
||||||
// /* in */ (() => lsType(() => a))
|
|
||||||
// /* out */ (() => fnType
|
|
||||||
// /* in */ (() => Int)
|
|
||||||
// /* out */ (() => a)
|
|
||||||
// )), GenericType),
|
|
||||||
|
|
||||||
// // [a] -> Int -> a -> [a]
|
|
||||||
// ...typedFnType(put, fnType =>
|
|
||||||
// makeGeneric(a =>
|
|
||||||
// fnType
|
|
||||||
// /* in */ (() => lsType(() => a))
|
|
||||||
// /* out */ (() => fnType
|
|
||||||
// /* in */ (() => Int)
|
|
||||||
// /* out */ (() => fnType
|
|
||||||
// /* in */ (() => a)
|
|
||||||
// /* out */ (() => lsType(() => a))
|
|
||||||
// )
|
|
||||||
// )), GenericType),
|
|
||||||
|
|
||||||
// // [a] -> a -> [a]
|
|
||||||
// ...typedFnType(push, fnType =>
|
|
||||||
// makeGeneric(a =>
|
|
||||||
// fnType
|
|
||||||
// (() => lsType(() => a))
|
|
||||||
// (() => fnType
|
|
||||||
// (() => a)
|
|
||||||
// (() => lsType(() => a))
|
|
||||||
// )
|
|
||||||
// ), GenericType),
|
|
||||||
|
|
||||||
// // [a] -> (a -> b) -> [b]
|
|
||||||
// ...typedFnType(map, fnType =>
|
|
||||||
// makeGeneric((a, b) =>
|
|
||||||
// fnType
|
|
||||||
// (() => lsType(() => a))
|
|
||||||
// (() => fnType
|
|
||||||
// (() => fnType(() => a)(() => b))
|
|
||||||
// (() => lsType(() => b))
|
|
||||||
// )), GenericType),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
|
||||||
import { Type, GenericType } from "../primitives/types.js";
|
|
||||||
import { newProduct, getLeft, getRight } from "./product.js";
|
|
||||||
import { typedFnType, prodType } from "./types.js";
|
|
||||||
|
|
||||||
export const ModuleProduct = {
|
|
||||||
l: [
|
|
||||||
// binary type constructor
|
|
||||||
// Type -> Type -> Type
|
|
||||||
...typedFnType(prodType, fnType => fnType(Type)(fnType(Type)(Type)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
// a -> b -> (a, b)
|
|
||||||
...typedFnType(newProduct, fnType => makeGeneric((a, b) => fnType(a)(fnType(b)(prodType(() => a)(() => b))
|
|
||||||
)
|
|
||||||
), GenericType),
|
|
||||||
|
|
||||||
// (a, b) -> a
|
|
||||||
...typedFnType(getLeft, fnType => makeGeneric((a, b) => fnType(prodType(() => a)(() => b))(a)
|
|
||||||
), GenericType),
|
|
||||||
|
|
||||||
// (a, b) -> b
|
|
||||||
...typedFnType(getRight, fnType => makeGeneric((a, b) => fnType(prodType(() => a)(() => b))(b)
|
|
||||||
), GenericType),
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import createRBTree from "functional-red-black-tree";
|
|
||||||
import { inspect } from "node:util";
|
|
||||||
|
|
||||||
export class RBTreeWrapper {
|
|
||||||
constructor(tree) {
|
|
||||||
this.tree = tree;
|
|
||||||
}
|
|
||||||
// pretty print to console
|
|
||||||
[inspect.custom](depth, options, inspect) {
|
|
||||||
const entries = [];
|
|
||||||
this.tree.forEach((key,val) => {entries.push(`${inspect(key)} => ${inspect(val)}`);});
|
|
||||||
return `RBTree(${this.tree.length}) {${entries.join(', ')}}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// (a -> a -> Int) -> Set(a)
|
|
||||||
export const emptySet = compareFn => new RBTreeWrapper(createRBTree((x, y) => compareFn(x)(y)));
|
|
||||||
|
|
||||||
|
|
||||||
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); });
|
|
||||||
};
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
|
||||||
import { Int } from "../primitives/types.js";
|
|
||||||
import { emptySet, has, add } from "./set.js";
|
|
||||||
import { fnType, setType } from "./types.js";
|
|
||||||
|
|
||||||
const emptySetType = makeGeneric(a =>
|
|
||||||
fnType
|
|
||||||
// comparison function:
|
|
||||||
(_ => fnType
|
|
||||||
(_ => a)
|
|
||||||
(_ => fnType(_ => a)(_ => Int)))
|
|
||||||
// the set:
|
|
||||||
(_ => setType(_ => a))
|
|
||||||
);
|
|
||||||
|
|
||||||
export const ModuleSet = {
|
|
||||||
l: [
|
|
||||||
// Type -> Type
|
|
||||||
...typedFnType(setType, fnType => fnType(_ => Type)(_ => Type)
|
|
||||||
),
|
|
||||||
|
|
||||||
{ i: emptySet, t: emptySetType },
|
|
||||||
{ i: emptySetType, t: GenericType },
|
|
||||||
|
|
||||||
...typedFnType(has, fnType => makeGeneric(a => fnType(_ => setType(_ => a))(_ => fnType(_ => a)(_ => Bool)
|
|
||||||
)), GenericType),
|
|
||||||
|
|
||||||
...typedFnType(add, fnType => makeGeneric(a => fnType(setType(_ => a))(fnType(a)(setType(_ => a))
|
|
||||||
)), GenericType),
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import { Unit } from "../primitives/types.js";
|
|
||||||
import { unit } from "../primitives/unit.js";
|
|
||||||
import { capitalizeFirstLetter } from "../util/util.js";
|
|
||||||
import { newProduct, getLeft, getRight } from "./product.js";
|
|
||||||
import { fnType, prodType } from "./types.js";
|
|
||||||
|
|
||||||
// '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;
|
|
||||||
}
|
|
||||||
const [field, ...rest] = fields;
|
|
||||||
const fieldType = getRight(field);
|
|
||||||
return prodType(() => fieldType)(() => structType(rest));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeConstructor = fields => {
|
|
||||||
const internal = (nParams, ret) => {
|
|
||||||
if (nParams === 0) {
|
|
||||||
const result = ret(unit);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return nextParam => {
|
|
||||||
const wrappedName = 'wrapped_' + ret.name;
|
|
||||||
const newRet = {
|
|
||||||
[wrappedName]: inner => newProduct(nextParam)(ret(inner)),
|
|
||||||
}[wrappedName];
|
|
||||||
return internal(nParams-1, newRet);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const id = x => x;
|
|
||||||
return internal(fields.length, id);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeConstructorType = type => fields => {
|
|
||||||
if (fields.length === 0) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
const [field, ...rest] = fields;
|
|
||||||
const fieldType = getRight(field);
|
|
||||||
return fnType(() => fieldType)(() => makeConstructorType(rest));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeGetters = fields => {
|
|
||||||
if (fields.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const [field, ...rest] = fields;
|
|
||||||
const fieldName = getLeft(field);
|
|
||||||
const getterName = `get${capitalizeFirstLetter(fieldName)}`;
|
|
||||||
return [
|
|
||||||
{ [getterName]: obj => getLeft(obj) }[getterName],
|
|
||||||
...makeGetters(rest).map(getter => ({[getter.name]: obj => getter(getRight(obj))}[getter.name])),
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeGettersTypes = type => fields => {
|
|
||||||
return fields.map(field => {
|
|
||||||
const fieldType = getRight(field);
|
|
||||||
return fnType(() => type)(() => fieldType);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
// 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 newLeft = left => ({t: "L", v: left }); // 't': tag, 'v': value
|
|
||||||
export const newRight = right => ({t: "R", v: right});
|
|
||||||
|
|
||||||
// signature:
|
|
||||||
// sum-type -> (leftType -> resultType, rightType -> resultType) -> resultType
|
|
||||||
export const match = sum => handlers =>
|
|
||||||
sum.t === "L"
|
|
||||||
? handlers.l(sum.v)
|
|
||||||
: handlers.r(sum.v);
|
|
||||||
|
|
||||||
export const ModuleSum = {l:[
|
|
||||||
// binary type constructor
|
|
||||||
// Type -> Type -> Type
|
|
||||||
...typedFnType(sumType, fnType =>
|
|
||||||
fnType
|
|
||||||
(() => Type)
|
|
||||||
(() => fnType
|
|
||||||
(() => Type)
|
|
||||||
(() => Type)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// // a -> a | b
|
|
||||||
// ...typedFnType(newLeft, fnType =>
|
|
||||||
// makeGeneric((a, b) =>
|
|
||||||
// fnType
|
|
||||||
// (a)
|
|
||||||
// (sumType(() => a)(() => b))
|
|
||||||
// ), GenericType),
|
|
||||||
|
|
||||||
// // b -> a | b
|
|
||||||
// ...typedFnType(newRight, fnType =>
|
|
||||||
// makeGeneric((a, b) =>
|
|
||||||
// fnType
|
|
||||||
// (b)
|
|
||||||
// (sumType(() => a)(() => b))
|
|
||||||
// ), GenericType),
|
|
||||||
|
|
||||||
// // a | b -> (a -> c, b-> c) -> c
|
|
||||||
// ...typedFnType(match, fnType =>
|
|
||||||
// makeGeneric((a, b, c) =>
|
|
||||||
// fnType
|
|
||||||
// (() => sumType(() => a)(() => b))
|
|
||||||
// (() => fnType
|
|
||||||
// (() => prodType
|
|
||||||
// (() => fnType(() => a)(() => c))
|
|
||||||
// (() => fnType(() => b)(() => c))
|
|
||||||
// )
|
|
||||||
// (() => c)
|
|
||||||
// )
|
|
||||||
// ), GenericType),
|
|
||||||
]};
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
// to break up dependency cycles, type constructors are defined in their own JS module
|
|
||||||
|
|
||||||
import { Type } from "../primitives/types.js";
|
|
||||||
import { getSymbol, makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
// Function type
|
|
||||||
|
|
||||||
// The registry ensures that we never accidentally create more than one JS object for the same function type.
|
|
||||||
// It is a cheap workaround for JS lacking customizable hash-functions and equality-testing-functions.
|
|
||||||
// This same pattern is repeated throughout the code for all non-nullary type constructors (list, sum, product, ...)
|
|
||||||
export const symbolFunction = Symbol('Function');
|
|
||||||
export const fnType = makeTypeConstructor(symbolFunction)(2);
|
|
||||||
|
|
||||||
export const isFunction = type => getSymbol(type) === symbolFunction;
|
|
||||||
|
|
||||||
// Convenience function. Creates a function type, and also create Type-links for the function type (being typed by Function) and for all the nested function types. Saves a lot of code writing.
|
|
||||||
export const typedFnType = (instance, callback, typeOfType = Type) => {
|
|
||||||
const fnTs = [];
|
|
||||||
const wrappedFnType = inType => outType => {
|
|
||||||
const fnT = fnType(() => inType)(() => outType);
|
|
||||||
fnTs.push(fnT);
|
|
||||||
return fnT;
|
|
||||||
};
|
|
||||||
const t = callback(wrappedFnType); // force evaluation
|
|
||||||
if (t.typeVars && typeOfType === Type) {
|
|
||||||
throw new Error("you probably meant to create a GenericType");
|
|
||||||
}
|
|
||||||
const res = [
|
|
||||||
{ i: instance, t },
|
|
||||||
{ i: t , t: typeOfType },
|
|
||||||
// ...fnTs.map(fnT => ({ i: fnT, t: Type })),
|
|
||||||
];
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sum type
|
|
||||||
|
|
||||||
export const symbolSum = Symbol("Sum");
|
|
||||||
export const sumType = makeTypeConstructor(symbolSum)(2);
|
|
||||||
|
|
||||||
// Product type
|
|
||||||
|
|
||||||
export const symbolProduct = Symbol("Product");
|
|
||||||
export const prodType = makeTypeConstructor(symbolProduct)(2);
|
|
||||||
|
|
||||||
// List type
|
|
||||||
|
|
||||||
export const symbolList = Symbol('List');
|
|
||||||
export const lsType = makeTypeConstructor(symbolList)(1);
|
|
||||||
|
|
||||||
// Set type
|
|
||||||
|
|
||||||
export const symbolSet = Symbol('Set');
|
|
||||||
export const setType = makeTypeConstructor(symbolSet)(1);
|
|
||||||
|
|
||||||
// Dict type
|
|
||||||
|
|
||||||
export const symbolDict = Symbol('Dict');
|
|
||||||
export const dictType = makeTypeConstructor(symbolDict)(2);
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
// import { DefaultMap } from "./util/defaultmap.js";
|
|
||||||
|
|
||||||
// const nullaryTypeConstructors = new DefaultMap(
|
|
||||||
// // symbol -> 0-ary type constructor (= a type, basically)
|
|
||||||
// symbol => ({
|
|
||||||
// symbol,
|
|
||||||
// params: [],
|
|
||||||
// }));
|
|
||||||
|
|
||||||
|
|
||||||
// // nAry: how many more type parameters to take
|
|
||||||
// // params: the type params we already took
|
|
||||||
// const makeTypeConstructorInternal = (symbol, nAry, params = []) => {
|
|
||||||
// // console.log("n_ary:", n_ary);
|
|
||||||
// if (nAry === 0 || nAry === 0n) {
|
|
||||||
// // a bit dirty, but otherwise OK
|
|
||||||
// if (params.length > 0) {
|
|
||||||
// const result = { symbol, params };
|
|
||||||
// // console.log("result:", result);
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// const result = nullaryTypeConstructors.getdefault(symbol, true)
|
|
||||||
// // console.log("result:", result);
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// // use DefaultMap, so we only construct every type once (saves memory)
|
|
||||||
// const m = new DefaultMap(typeParam => makeTypeConstructorInternal(symbol, nAry - 1, params.concat([typeParam])));
|
|
||||||
// const fnName = 'make'+symbol.description+'Type';
|
|
||||||
// return {
|
|
||||||
// [fnName]: typeParam => m.getdefault(typeParam, true),
|
|
||||||
// }[fnName];
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
const __makeTypeConstructor = (symbol, nAry, params) => {
|
|
||||||
if (nAry === 0) {
|
|
||||||
return { symbol, params };
|
|
||||||
}
|
|
||||||
// only for debugging, do we give the function a name
|
|
||||||
const fName = `${symbol.description.toLowerCase()}Type${params.length>0?params.length:''}`;
|
|
||||||
return {
|
|
||||||
[fName]: typeParam => {
|
|
||||||
if (typeof typeParam !== 'function') {
|
|
||||||
throw new Error("all type params must be functions");
|
|
||||||
}
|
|
||||||
return __makeTypeConstructor(symbol, nAry-1, params.concat([typeParam]));
|
|
||||||
}
|
|
||||||
}[fName];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new nominal type
|
|
||||||
// export const makeTypeConstructor = symbol => nAry => makeTypeConstructorInternal(symbol, nAry);
|
|
||||||
export const makeTypeConstructor = symbol => nAry => __makeTypeConstructor(symbol, nAry, []);
|
|
||||||
|
|
||||||
export const getSymbol = type => type.symbol;
|
|
||||||
export const getParams = type => ({ l: type.params });
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
import { makeGeneric } from "../generics/generics";
|
|
||||||
import { GenericType, SymbolT, Type, Unit } from "../primitives/types";
|
|
||||||
import { typedFnType } from "../structures/types";
|
|
||||||
import { Bool, Byte, Char, Double, Int } from "../primitives/types";
|
|
||||||
import { deepEqual } from "../util/util";
|
|
||||||
import { eqDictType } from "./eq_dict";
|
|
||||||
|
|
||||||
export const getEq = numDict => numDict.eq;
|
|
||||||
|
|
||||||
export const ModuleEq = {l:[
|
|
||||||
// type constructor: Type -> Type
|
|
||||||
...typedFnType(eqDictType, fnType => fnType(() => Type)(() => Type)),
|
|
||||||
|
|
||||||
// (EqDict a) -> a -> a -> Bool
|
|
||||||
...typedFnType(getEq, fnType =>
|
|
||||||
makeGeneric(a =>
|
|
||||||
fnType
|
|
||||||
(eqDictType(a))
|
|
||||||
(fnType
|
|
||||||
(a)
|
|
||||||
(fnType
|
|
||||||
(a)
|
|
||||||
(Bool)
|
|
||||||
)
|
|
||||||
)), GenericType),
|
|
||||||
]};
|
|
||||||
|
|
||||||
// all our data (and types) are encoded such that we can test equality the same way:
|
|
||||||
|
|
||||||
const eq = x => y => deepEqual(x,y);
|
|
||||||
|
|
||||||
const eqDict = {eq};
|
|
||||||
|
|
||||||
export const EqInstances = new Map([
|
|
||||||
[Int , eqDict],
|
|
||||||
[Bool , eqDict],
|
|
||||||
[Double , eqDict],
|
|
||||||
[Byte , eqDict],
|
|
||||||
[Char , eqDict],
|
|
||||||
[Unit , eqDict],
|
|
||||||
[Type , eqDict],
|
|
||||||
[SymbolT, eqDict],
|
|
||||||
]);
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
const eqDictSymbol = Symbol('EqDict');
|
|
||||||
export const eqDictType = makeTypeConstructor(eqDictSymbol)(1);
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
|
||||||
import { addDouble, mulDouble } from "../primitives/double.js";
|
|
||||||
import { addInt, mulInt } from "../primitives/int.js";
|
|
||||||
import { Type } from "../primitives/types.js";
|
|
||||||
import { typedFnType, typedFnType2 } from "../structures/types.js";
|
|
||||||
import { Double, Int } from "../primitives/types.js";
|
|
||||||
import { numDictType } from "./num_type.js";
|
|
||||||
|
|
||||||
export const getAdd = numDict => numDict.add;
|
|
||||||
export const getMul = numDict => numDict.mul;
|
|
||||||
|
|
||||||
// getAdd and getMul have same (generic) type:
|
|
||||||
// NumDict a -> a -> a -> a
|
|
||||||
const [getAddMulFnType, typesOfFns] = typedFnType2(fnType =>
|
|
||||||
makeGeneric(a =>
|
|
||||||
fnType
|
|
||||||
(numDictType(a))
|
|
||||||
(fnType
|
|
||||||
(a)
|
|
||||||
(fnType(() => a)(() => a))
|
|
||||||
)));
|
|
||||||
|
|
||||||
export const ModuleNum = {l:[
|
|
||||||
...typedFnType(numDictType, fnType => fnType({in: Type, out: Type})),
|
|
||||||
|
|
||||||
{i: getAdd, t: getAddMulFnType},
|
|
||||||
{i: getMul, t: getAddMulFnType},
|
|
||||||
|
|
||||||
...typesOfFns,
|
|
||||||
]};
|
|
||||||
|
|
||||||
|
|
||||||
const IntNumDict = {
|
|
||||||
add: addInt,
|
|
||||||
mul: mulInt,
|
|
||||||
};
|
|
||||||
|
|
||||||
const DoubleNumDict = {
|
|
||||||
add: addDouble,
|
|
||||||
mul: mulDouble,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ModuleNumInstances = {l:[
|
|
||||||
{i: IntNumDict , t: numDictType(Int)},
|
|
||||||
{i: DoubleNumDict, t: numDictType(Double)},
|
|
||||||
]};
|
|
||||||
|
|
||||||
// mapping from type to type class implementation
|
|
||||||
// in Haskell, such a mapping is global (for the entire application being compiled), and every type can implement every type class at most once.
|
|
||||||
// in Lean, such mappings can be local, and there can be multiple implementations per (type, type class).
|
|
||||||
// We have to follow Lean's approach, because in DOPE, there is no such thing as a "global scope". Every dependency is explicit, and type class resolution is just a function that always depends on a specific mapping:
|
|
||||||
export const NumInstances = new Map([
|
|
||||||
[Int , IntNumDict ],
|
|
||||||
[Double, DoubleNumDict],
|
|
||||||
]);
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
const numDictSymbol = Symbol("NumDict");
|
|
||||||
export const numDictType = makeTypeConstructor(numDictSymbol)(1);
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import { cart2Str, NPoint2DCartesian, NPoint2DPolar, polar2Str } from "../lib/point.js";
|
|
||||||
import { Type } from "../primitives/types.js";
|
|
||||||
import { prettyT } from "../structures/types.js";
|
|
||||||
|
|
||||||
export const ShowInstances = new Map([
|
|
||||||
[Type , {show: prettyT}],
|
|
||||||
[NPoint2DCartesian, {show: cart2Str}],
|
|
||||||
[NPoint2DPolar , {show: polar2Str}],
|
|
||||||
]);
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
|
||||||
|
|
||||||
const showDictSymbol = Symbol('ShowDict');
|
|
||||||
export const showDictType = makeTypeConstructor(showDictSymbol)(1);
|
|
||||||
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
|
|
||||||
export class DefaultMap {
|
|
||||||
constructor(defaultValue, ...rest) {
|
|
||||||
this.defaultValue = defaultValue;
|
|
||||||
this.m = new Map(rest);
|
|
||||||
}
|
|
||||||
getdefault(key, addToMapping = false) {
|
|
||||||
return this.m.get(key) || (() => {
|
|
||||||
const val = this.defaultValue(key);
|
|
||||||
if (addToMapping)
|
|
||||||
this.m.set(key, val);
|
|
||||||
return val;
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
entries() {
|
|
||||||
return this.m.entries();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
101
util/util.js
101
util/util.js
|
|
@ -1,101 +0,0 @@
|
||||||
import { lsType, setType } from "../structures/types.js";
|
|
||||||
import { pretty } from "./pretty.js";
|
|
||||||
|
|
||||||
// re-inventing the wheel:
|
|
||||||
export function deepEqual(a, b) {
|
|
||||||
if (a === b) return true; // <- shallow equality and primitives
|
|
||||||
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Array.isArray(a) && Array.isArray(b)) {
|
|
||||||
if (a.length !== b.length) return false;
|
|
||||||
for (let i = 0; i < a.length; i++) {
|
|
||||||
if (!deepEqual(a[i], b[i])) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (a instanceof Set) {
|
|
||||||
if (!(b instanceof Set)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (a.size !== b.size) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const entry of a) {
|
|
||||||
if (!b.has(entry)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const keysA = Object.keys(a);
|
|
||||||
const keysB = Object.keys(b);
|
|
||||||
if (keysA.length !== keysB.length) return false;
|
|
||||||
for (let key of keysA) {
|
|
||||||
if (!keysB.includes(key) || !deepEqual(a[key], b[key])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// zip two arrays
|
|
||||||
export function zip(a, b) {
|
|
||||||
return a.map((k, i) => [k, b[i]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function capitalizeFirstLetter(val) {
|
|
||||||
return String(val).charAt(0).toUpperCase() + String(val).slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const _mapRecursiveStructure = mapping => transform => root => {
|
|
||||||
const found = mapping.get(root);
|
|
||||||
if (found) {
|
|
||||||
// already mapped
|
|
||||||
// return existing result to prevent endless recursion
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
// note the indirection (wrapped in lamda), this allows the user to recursively map the children (which may refer to the root) without yet having finished mapping the root.
|
|
||||||
let memo;
|
|
||||||
const result = () => {
|
|
||||||
// memoization is necessary for correctness
|
|
||||||
return memo || (memo = transform(root, _mapRecursiveStructure(mapping)(transform)));
|
|
||||||
};
|
|
||||||
mapping.set(root, result);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const mapRecursiveStructure = _mapRecursiveStructure(new Map());
|
|
||||||
|
|
||||||
const _transformType = mapping => transform => type => {
|
|
||||||
const found = mapping.get(type);
|
|
||||||
if (found) {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
const mapped = transform(type, _transformType(mapping)(transform));
|
|
||||||
mapping.set(type, mapped);
|
|
||||||
return mapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const transformType = _transformType(new Map());
|
|
||||||
|
|
||||||
const __memo = () => {
|
|
||||||
let memo;
|
|
||||||
return fn => memo || (memo = fn());
|
|
||||||
}
|
|
||||||
export const memo = fn => {
|
|
||||||
return __memo()(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// let infiniteSet = mapRecursiveStructure((type, map) => {
|
|
||||||
// const ps = [];
|
|
||||||
// for (const p of type.params) {
|
|
||||||
// ps.push(map(p()));
|
|
||||||
// }
|
|
||||||
// return setType(ps[0]);
|
|
||||||
// })(infiniteList)();
|
|
||||||
// console.log(infiniteSet);
|
|
||||||
// // while (true) {
|
|
||||||
// // console.log(infiniteSet);
|
|
||||||
// // infiniteSet = infiniteSet.params[0]();
|
|
||||||
// // }
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue