lotta progress

This commit is contained in:
Joeri Exelmans 2025-03-23 13:25:47 +01:00
parent 29d20b2273
commit bc91d9bf39
27 changed files with 317 additions and 475 deletions

View file

@ -1,42 +1,6 @@
import { DefaultMap } from "../util.js";
const symbolFunction = Symbol('Function');
// 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, ...)
const fnTypeRegistry = new DefaultMap(inType => new DefaultMap(outType => ({
symbol: symbolFunction,
params: [inType, outType],
})));
// type constructor for function types
export const fnType = inType => outType => fnTypeRegistry.getdefault(inType, true).getdefault(outType, true);
// Wrapper around function below.
export const typedFnType = (instance, callback) => {
const [t, typesOfFns] = typedFnType2(callback);
const res = [
{ i: instance, t },
...typesOfFns,
];
return res;
};
// Create 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 typedFnType2 = callback => {
const fnTs = [];
const wrappedFnType = ({ in: inType, out: outType }) => {
const fnT = fnType({ in: inType, out: outType });
fnTs.push(fnT);
return fnT;
};
const t = callback(wrappedFnType); // force evaluation
return [
t,
fnTs.map(fnT => ({ i: fnT, t: Function })),
];
};
import { Type } from "../type.js";
import { typedFnType } from "./types.js";
import { fnType } from "./types.js";
export const ModuleFunction = {l:[
@ -48,4 +12,4 @@ export const ModuleFunction = {l:[
/* out */ (Type)
)
),
]};
]};

View file

@ -1,18 +1,8 @@
import { typedFnType } from "./function.js";
import { Type } from "../metacircular.js";
import { Int } from "../primitives/symbols.js";
import { DefaultMap } from "../util.js";
import { typedFnType } from "./types.js";
import { Type } from "../type.js";
import { Int } from "../primitives/types.js";
import { makeGeneric } from "../generics/generics.js";
const symbolList = Symbol('List');
const listTypeRegistry = new DefaultMap(elementType => ({
symbol: symbolList,
params: [elementType],
}));
// type constructor
export const lsType = elementType => listTypeRegistry.getdefault(elementType, true);
import { lsType } from "./types.js";
// 'normal' implementation
const emptyList = {l:[]};
@ -22,30 +12,35 @@ const put = ls => i => elem => ({l: ls.l.with(Number(i), elem)});
export const ModuleList = {l:[
// Type -> Type
...typedFnType(lsType, fnType => fnType
/* in */ (Type)
/* out */ (Type)
...typedFnType(lsType, fnType =>
fnType
/* in */ (Type)
/* out */ (Type)
),
// [a]
{i: emptyList, t: makeGeneric(a => lsType(a))},
// [a] -> Int -> a
...typedFnType(get, fnType => makeGeneric(a => fnType
/* in */ (lsType(a))
/* out */ (fnType
/* in */ (Int)
/* out */ (a)
))),
...typedFnType(get, fnType =>
makeGeneric(a =>
fnType
/* in */ (lsType(a))
/* out */ (fnType
/* in */ (Int)
/* out */ (a)
))),
// [a] -> Int -> a -> [a]
...typedFnType(put, fnType => makeGeneric(a => fnType
/* in */ (lsType(a))
/* out */ (fnType
/* in */ (Int)
/* out */ (fnType
/* in */ (a)
/* out */ (lsType(a))
)
))),
...typedFnType(put, fnType =>
makeGeneric(a =>
fnType
/* in */ (lsType(a))
/* out */ (fnType
/* in */ (Int)
/* out */ (fnType
/* in */ (a)
/* out */ (lsType(a))
)
))),
]};

View file

@ -1,8 +0,0 @@
import { makeListModule } from "../list.js";
import { Typed } from "../../typed.js";
import { lsType } from "../list.js";
// just an alias
export const Module = lsType(Typed); // a Module is a list of Typeds
export const ModuleModule = makeListModule(Typed); // the module containing operations on Module

View file

@ -1,8 +0,0 @@
import { Char } from "../../primitives/symbols.js";
import { lsType } from "../list.js";
import { makeListModule } from "../list.js";
// just an alias
export const String = lsType(Char);
export const ModuleString = makeListModule(Char);

View file

@ -1,10 +0,0 @@
import { Type } from "../metacircular.js";
import { String } from "../structures/list_types/string.js";
import { prodType } from "./product.js";
import { makeProductType } from "./product.js";
export const NominalType = prodType(String, Type);
export const ModuleNominalType = makeProductType(String, Type);
export const nominalType = ModuleNominalType.l[5].i; // dirty!!

View file

@ -1,16 +1,7 @@
import { makeGeneric } from "../generics/generics.js";
import { Type } from "../metacircular.js";
import { DefaultMap } from "../util.js";
import { typedFnType } from "./function.js";
const symbolProduct = Symbol("Product");
const productTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({
symbol: symbolProduct,
params: [leftType, rightType],
})));
// type constructor
export const prodType = leftType => rightType => productTypeRegistry.getdefault(leftType, true).getdefault(rightType, true);
import { Type } from "../type.js";
import { typedFnType } from "./types.js";
import { prodType } from "./types.js";
// In JS, all products are encoded in the same way:
const constructor = left => right => ({left, right});
@ -20,32 +11,39 @@ const getRight = product => product.right;
export const ModuleProduct = {l: [
// binary type constructor
// Type -> Type -> Type
...typedFnType(prodType, fnType => fnType
(Type)
(fnType
...typedFnType(prodType, fnType =>
fnType
(Type)
(Type)
)
(fnType
(Type)
(Type)
)
),
// a -> b -> (a, b)
...typedFnType(constructor, fnType => makeGeneric((a, b) => fnType
(a)
(fnType
(b)
(prodType(a)(b))
)
)),
...typedFnType(constructor, fnType =>
makeGeneric((a, b) =>
fnType
(a)
(fnType
(b)
(prodType(a)(b))
)
)),
// (a, b) -> a
...typedFnType(getLeft, fnType => makeGeneric((a, b) => fnType
(prodType(a)(b))
(a)
)),
...typedFnType(getLeft, fnType =>
makeGeneric((a, b) =>
fnType
(prodType(a)(b))
(a)
)),
// (a, b) -> b
...typedFnType(getRight, fnType => makeGeneric((a, b) => fnType
(prodType(a)(b))
(b)
)),
...typedFnType(getRight, fnType =>
makeGeneric((a, b) =>
fnType
(prodType(a)(b))
(b)
)),
]};

View file

@ -1,18 +1,8 @@
import { prodType } from "./product.js";
import { Type } from "../metacircular.js";
import { DefaultMap } from "../util.js";
import { typedFnType } from "./function.js";
import { prodType } from "./types.js";
import { Type } from "../type.js";
import { typedFnType } from "./types.js";
import { makeGeneric } from "../generics/generics.js";
const symbolSum = Symbol("Sum");
const sumTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({
symbol: symbolSum,
params: [leftType, rightType],
})));
// type constructor
export const sumType = leftType => rightType => sumTypeRegistry.getdefault(leftType, true).getdefault(rightType, true);
import { sumType } from "./types.js";
const constructorLeft = left => ({variant: "L", value: left });
const constructorRight = right => ({variant: "R", value: right});
@ -26,35 +16,42 @@ const match = sum => handlers => sum.variant === "L"
export const ModuleSum = {l:[
// binary type constructor
// Type -> Type -> Type
...typedFnType(sumType, fnType => fnType
(Type)
(fnType
(Type)
...typedFnType(sumType, fnType =>
fnType
(Type)
(fnType
(Type)
(Type)
),
),
),
// a -> a | b
...typedFnType(constructorLeft, fnType => makeGeneric((a, b) => fnType
(a)
(sumType(a)(b))
)),
...typedFnType(constructorLeft, fnType =>
makeGeneric((a, b) =>
fnType
(a)
(sumType(a)(b))
)),
// b -> a | b
...typedFnType(constructorRight, fnType => makeGeneric((a, b) => fnType
(b)
(sumType(a)(b))
)),
...typedFnType(constructorRight, fnType =>
makeGeneric((a, b) =>
fnType
(b)
(sumType(a)(b))
)),
// 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)
)
)),
...typedFnType(match, fnType =>
makeGeneric((a, b, c) =>
fnType
(sumType(a)(b))
(fnType
(prodType
(fnType(a)(c))
(fnType(b)(c))
)
(c)
)
)),
]};

74
structures/types.js Normal file
View file

@ -0,0 +1,74 @@
// to break up dependency cycles, type constructors are defined in their own JS module
import { DefaultMap } from "../util.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');
const fnTypeRegistry = new DefaultMap(inType => new DefaultMap(outType => ({
symbol: symbolFunction,
params: [inType, outType],
})));
// type constructor
export const fnType = inType => outType => fnTypeRegistry.getdefault(inType, true).getdefault(outType, true);
// Convenience function. Wrapper around function below.
export const typedFnType = (instance, callback) => {
const [t, typesOfFns] = typedFnType2(callback);
const res = [
{ i: instance, t },
...typesOfFns,
];
return res;
};
// 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 typedFnType2 = callback => {
const fnTs = [];
const wrappedFnType = inType => outType => {
const fnT = fnType(inType)(outType);
fnTs.push(fnT);
return fnT;
};
const t = callback(wrappedFnType); // force evaluation
return [
t,
fnTs.map(fnT => ({ i: fnT, t: Function })),
];
};
// Sum type
const symbolSum = Symbol("Sum");
const sumTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({
symbol: symbolSum,
params: [leftType, rightType],
})));
// type constructor
export const sumType = leftType => rightType => sumTypeRegistry.getdefault(leftType, true).getdefault(rightType, true);
// Product type
const symbolProduct = Symbol("Product");
const productTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({
symbol: symbolProduct,
params: [leftType, rightType],
})));
// type constructor
export const prodType = leftType => rightType => productTypeRegistry.getdefault(leftType, true).getdefault(rightType, true);
// List type
const symbolList = Symbol('List');
const listTypeRegistry = new DefaultMap(elementType => ({
symbol: symbolList,
params: [elementType],
}));
// type constructor
export const lsType = elementType => listTypeRegistry.getdefault(elementType, true);