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,4 +1,4 @@
import { eqType } from "../metacircular.js";
import { eqType } from "../type.js";
import { pretty, zip } from "../util.js";
// constructor for generic types

View file

@ -1,5 +1,5 @@
import { Bool, Int } from "../primitives/symbols.js";
import { lsType } from "../structures/list.js";
import { Bool, Int } from "../primitives/types.js";
import { lsType } from "../structures/types.js";
import { fnType } from "../structures/function.js";
import { assign, makeGeneric, unify } from "./generics.js";
import { pretty } from "../util.js";

View file

@ -1,16 +0,0 @@
import { fnType, lsType } from "../type_registry.js";
import {Type, Function} from "../metacircular.js";
import { Byte } from "../primitives/symbols.js";
export const Serializable = Symbol('Serializable');
const ListOfByte = lsType(Byte);
const serializeFnType = fnType({in: Serializable, out: ListOfByte});
const deserializeFnType = fnType({in: ListOfByte, out: Serializable});
export const ModuleSerializable = {l:[
{i: Serializable , t: Type},
{i: serializeFnType , t: Function},
{i: deserializeFnType, t: Function},
]};

View file

@ -1,39 +0,0 @@
// import { fnType } from "../metacircular.js";
// import {Function} from "../metacircular.js";
// // import {Typed} from "../typed.js";
// // import {Bool} from "../primitives/symbols.js";
// // import {deepEqual} from "../util.js";
// // import {Conformable, conformanceCheck} from "../typeclasses/conformable.js";
// // // Identity function
// // const idBoundToType = Symbol('idBoundToType');
// // const id = type => x => x;
// // const idFn = {name: "id", inType: Type, outType: {inType: Type, outType: Type}, fn: id};
// // const idIsConform = {
// // name: "isConform",
// // inType: idBoundToType,
// // outType: Bool,
// // fn: fn => deepEqual(fnIn(fn), fnOut(fn))
// // }
// // export const ModuleId = [
// // {i: idBoundToType, t: Type},
// // {i: idFn, t: Function},
// // ... makeTypedFnInOut({f: idBoundToType, inType: Type, outType: Type}),
// // {i: idBoundToType, t: Conformable},
// // {i: idIsConform, t: conformanceCheck},
// // ];
// // generates explicitly typed id-function
// export const makeIdFn = typ => {
// const Typ_to_Typ = fnType({in: typ, out: typ});
// const id = x => x;
// return {l:[
// {i: id , t: Typ_to_Typ},
// {i: Typ_to_Typ, t: Function},
// ]};
// };

View file

@ -1,65 +1,72 @@
import { Double } from "../primitives/symbols.js";
import { nominalType, NominalType } from "../structures/nominal_type.js";
import { makeProductType } from "../structures/product.js";
import { typedFnType } from "../metacircular.js";
import { prodType } from "../structures/product.js";
import { Type } from "../type.js";
import { typedFnType } from "../structures/types.js"
import { Double } from "../primitives/types.js";
const PointCartesian2D = nominalType(
"PointCartesian2D")
// just a unique number, to make sure that our type is only equal to itself
// BigInt("0xBBAAD62B10EE21993BA690A732DA2A6875CE4B6F5E7139D5AEC9FD887F9D24A8"))
(prodType(Double, Double));
// Create nominal types
export const PointCartesian2D = { symbol: Symbol('PointCartesian2D'), params: [] };
export const PointPolar2D = { symbol: Symbol('PointPolar2D') , params: [] };
const PointPolar2D = nominalType(
"PointPolar2D")
// just a unique number, to make sure that our type is only equal to itself
// BigInt("0x31CDAB4B3D84C4EB27D3C111FD7580E533268B72E05BD694F8B262913E018B72"))
(prodType(Double, Double));
export const cart2polar = ({left: x, right: y}) => {
export const cart2polar = ({x, y}) => {
const r = Math.sqrt(x*x + y*y);
const θ = Math.atan(y/x);
return {left: r, right: θ};
return {r, θ};
};
export const polar2cart = ({left: r, right: θ}) => {
export const polar2cart = ({r, θ}) => {
const x = r * Math.cos(θ);
const y = r * Math.sin(θ);
return {left: x, right: y};
return {x, y};
}
export const translate = dx => dy => ({left: x, right: y}) => {
export const translate = dx => dy => ({x, y}) => {
return {left: x+dx, right: y+dy};
}
export const rotate = => ({left: r, right: θ}) => {
return {left: r, right: θ+};
export const rotate = => ({r, θ}) => {
return {r, θ: θ+};
}
export const scale = dr => ({left: r, right: θ}) => {
return {left: r+dr, right: θ};
export const scale = dr => ({r, θ}) => {
return {r: r+dr, θ};
}
const examplePoint = {left: 1, right: 2};
const examplePoint = {x: 1, y: 2};
export const ModulePoint = {l:[
{i: -1 , t: Double},
{i: 2 , t: Double},
{i: examplePoint , t: PointCartesian2D},
{i: examplePoint , t: prodType(Double, Double)},
...makeProductType(Double, Double).l,
{i: PointCartesian2D , t: Type},
{i: PointPolar2D , t: Type},
{i: PointCartesian2D , t: NominalType},
{i: PointPolar2D , t: NominalType},
...typedFnType(cart2polar, fnType => fnType(PointCartesian2D)(PointPolar2D)),
...typedFnType(cart2polar, fnType => fnType({in: PointCartesian2D, out: PointPolar2D})),
...typedFnType(polar2cart, fnType => fnType(PointPolar2D)(PointCartesian2D)),
...typedFnType(polar2cart, fnType => fnType({in: PointPolar2D, out: PointCartesian2D})),
// Double -> Double -> PointCartesian2D -> PointCartesian2D
...typedFnType(translate, fnType =>
fnType
(Double)
(fnType
(Double)
(fnType
(PointCartesian2D)
(PointCartesian2D)))),
...typedFnType(translate , fnType => fnType({in: Double, out: fnType({in: Double, out: fnType({in: PointCartesian2D, out: PointCartesian2D})})})),
// Double -> PointPolar2D -> PointPolar2D
...typedFnType(rotate, fnType =>
fnType
(Double)
(fnType
(PointPolar2D)
(PointPolar2D))),
...typedFnType(rotate, fnType => fnType({in: Double, out: fnType({in: PointPolar2D, out: PointPolar2D})})),
...typedFnType(scale, fnType => fnType({in: Double, out: fnType({in: PointPolar2D, out: PointPolar2D})})),
// Double -> PointPolar2D -> PointPolar2D
...typedFnType(scale, fnType =>
fnType
(Double)
(fnType
(PointPolar2D)
(PointPolar2D))),
]};

View file

@ -1,50 +0,0 @@
// import { getIn, getOut } from "../metacircular.js";
// import {Module} from "../structures/list_types/module.js";
// import { deepEqual } from "../util.js";
// import { Typed } from "../typed.js";
// import { fnType } from "../metacircular.js";
// import {Num, NumDict, getType, getMul} from "../typeclasses/num.js";
// const squareBoundToType = Symbol('squareBoundToType'); // the type of e.g., squareOfInt
// // returning a function + its signature is the only way to make the outType of square depend on its input
// const square = numDict => {
// const typ = getType(numDict);
// const mul = getMul(numDict);
// return {
// name: "squareOf:" + typ.toString(),
// inType: typ,
// outType: typ,
// fn: x => mul(x)(x),
// };
// };
// export const ModuleSquare = [
// {i: squareBoundToType, t: Type},
// {i: {name: "square", inType: NumDict, outType: squareBoundToType, fn: square}, t: Function},
// ...makeTypedFnInOut({f: squareBoundToType, inType: Num, outType: Num}),
// ];
// export const makeSquare = ({i: mul, t: mulFunction}) => {
// const numType = getIn(mulFunction);
// const boundMulFunction = getOut(mulFunction);
// if (!deepEqual(getOut(boundMulFunction), numType) || !deepEqual(getIn(boundMulFunction), numType)) {
// console.log(getOut(boundMulFunction), getIn(boundMulFunction), numType);
// throw new Error("invalid signature");
// }
// const square = x => mul(x)(x);
// const squareFunction = fnType({in: numType, out: numType});
// return {l:[
// {i: square , t: squareFunction},
// {i: squareFunction, t: Function},
// ]};
// };
// const makeSquareType = fnType({in: Typed, out: Module});
// export const ModuleSquare = {l:[
// {i: makeSquare , t: makeSquareType},
// {i: makeSquareType, t: Function},
// ]};

98
main.js
View file

@ -1,38 +1,28 @@
import {Function, ModuleMetaCircular, getIn, getOut} from "./metacircular.js";
import {ModuleTyped} from "./typed.js";
import {ModuleBool} from "./primitives/bool.js";
import { Double_to_Double_to_Double, ModuleDouble, mulDouble } from "./primitives/double.js";
import {Int_to_Int_to_Int, ModuleInt, mulInt} from "./primitives/int.js";
import { makeSquare } from "./lib/square.js";
import {makeIdFn} from "./lib/id.js";
import {DefaultMap, pretty} from "./util.js";
import { ModuleModule } from "./structures/list_types/module.js";
import { ModuleList } from "./structures/list.js";
import {Int, Bool, Double, Byte} from "./primitives/symbols.js";
import { makeListModule } from "./structures/list_common.js";
import { makeProductType } from "./structures/product.js";
import { makeSumType } from "./structures/sum.js";
import { sumType } from "./structures/sum.js";
import { prodType } from "./structures/product.js";
import { lsType } from "./structures/list_common.js";
import { select } from '@inquirer/prompts';
import { ModulePoint } from "./lib/point.js";
import { DefaultMap, pretty } from './util.js';
import { symbolFunction } from './structures/types.js';
import { ModuleStd } from './stdlib.js';
class Context {
constructor(mod) {
this.functionsFrom = new DefaultMap(() => []);
this.functionsTo = new DefaultMap(() => []);
this.types = new DefaultMap(() => []);
this.instances = new DefaultMap(() => []);
this.functionsFrom = new DefaultMap(() => []); // type to outgoing function
this.functionsTo = new DefaultMap(() => []); // type to incoming function
this.types = new DefaultMap(() => []); // instance to type
this.instances = new DefaultMap(() => []); // type to instance
for (const {i, t} of mod.l) {
this.types.getdefault(i, true).push(t);
this.instances.getdefault(t, true).push(i);
}
for (const fnType of this.instances.getdefault(Function)) {
for (const fn of this.instances.getdefault(fnType)) {
this.functionsFrom.getdefault(getIn(fnType), true).push(fn);
this.functionsTo.getdefault(getOut(fnType), true).push(fn);
for (const [i, types] of this.instances.m.entries()) {
for (const t of types) {
if (t.symbol === symbolFunction) {
// 'i' is a function
this.functionsFrom.getdefault(t.params[0], true).push(i);
this.functionsFrom.getdefault(t.params[1], true).push(i);
}
}
}
}
@ -68,62 +58,8 @@ class Context {
// }
}
const ListOfDouble = lsType(Double);
const ListOfDoubleModule = makeListModule(Double);
const ListOfListOfDouble = lsType(ListOfDouble);
const ListOfListOfDoubleModule = makeListModule(ListOfDouble);
const ListOfByte = lsType(Byte);
const ListOfByteModule = makeListModule(Byte);
const ModuleValues = {l:[
{i: 0n, t: Int},
{i: 42n, t: Int},
{i: false, t: Bool},
{i: 3.14159265359, t: Double},
{i: {l:[4.2, 7.6]} , t: ListOfDouble},
{i: {l:[{l:[4.2, 7.6]}, {l:[4.3]}]}, t: ListOfListOfDouble},
{i: new Uint8Array([1,2,3]), t: ListOfByte},
// i'm lazy
...ListOfDoubleModule.l,
...ListOfListOfDoubleModule.l,
...ListOfByteModule.l,
...makeProductType(Int, Bool).l,
...makeSumType(Int, Bool).l,
{i: {left: 42n, right: true}, t: prodType(Int, Bool)},
{i: {variant: "L", value: 100n}, t: sumType(Int, Bool)},
]};
import { select } from '@inquirer/prompts';
import { ModulePoint } from "./lib/point.js";
const ctx = new Context({l:[
// ...ModuleMetaCircular.l,
// ...ModuleTyped.l,
// // ...ModuleConformable,
// // ...ModuleConformanceCheckConforms,
// // ...ModuleNum,
// // ...ModuleEq,
// ...ModuleBool.l,
// ...ModuleInt.l,
// ...ModuleDouble.l,
// // ...ModuleSquare,
// // ...ModuleList,
// ...makeIdFn(Int).l,
// ...makeSquare({i: mulInt, t: Int_to_Int_to_Int}).l,
// ...makeSquare({i: mulDouble, t: Double_to_Double_to_Double}).l,
// ...ModuleList.l,
// ...ModuleValues.l,
// ...ModuleModule.l,
...ModuleStd.l,
...ModulePoint.l,
]});

View file

@ -1,15 +1,15 @@
import { fnType } from "../structures/function.js";
import { Type } from "../metacircular.js";
import { Bool } from "./symbols.js";
import { fnType, typedFnType } from "../structures/types.js";
import { Type } from "../type.js";
import { Bool } from "./types.js";
const eqBool = x => y => x === y;
const Bool_to_Bool = fnType({in: Bool, out: Bool});
const Bool_to_Bool_to_Bool = fnType({in: Bool, out: Bool_to_Bool});
export const ModuleBool = {l:[
{i: Bool , t: Type },
{i: Bool_to_Bool , t: Function },
{i: Bool_to_Bool_to_Bool , t: Function },
{i: eqBool , t: Bool_to_Bool_to_Bool },
{i: true , t: Bool},
{i: false, t: Bool},
{i: Bool , t: Type},
// Bool -> Bool -> Bool
...typedFnType(eqBool, fnType => fnType(Bool)(fnType(Bool)(Bool))),
]};

View file

@ -1,15 +1,11 @@
import { fnType } from "../structures/function.js";
import { Type } from "../metacircular.js";
import {Byte, Bool} from "./symbols.js";
import { typedFnType } from "../structures/types.js";
import { Type } from "../type.js";
import {Byte, Bool} from "./types.js";
const eqByte = x => y => x === y;
const Byte_to_Bool = fnType({in: Byte, out: Bool});
const Byte_to_Byte_to_Bool = fnType({in: Byte, out: Byte_to_Bool});
export const ModuleByte = {l:[
{i: Byte , t: Type },
{i: Byte_to_Bool , t: Function },
{i: Byte_to_Byte_to_Bool , t: Function },
{i: eqByte , t: Byte_to_Byte_to_Bool },
...typedFnType(eqByte, fnType => fnType(Byte)(fnType(Byte)(Bool))),
]};

View file

@ -1,6 +1,6 @@
import { typedFnType } from "../structures/function.js";
import { Type } from "../metacircular.js";
import {Char, Bool} from "./symbols.js";
import { typedFnType } from "../structures/types.js";
import { Type } from "../type.js";
import {Char, Bool} from "./types.js";
const eq = x => y => x === y;

View file

@ -1,26 +1,15 @@
import { fnType } from "../structures/function.js";
import { Type } from "../metacircular.js";
import {Bool, Double} from "./symbols.js";
import { typedFnType } from "../structures/types.js";
import { Type } from "../type.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;
const Double_to_Double = fnType({in: Double, out: Double});
const Double_to_Bool = fnType({in: Double, out: Bool});
export const Double_to_Double_to_Double = fnType({in: Double, out: Double_to_Double});
export const Double_to_Double_to_Bool = fnType({in: Double, out: Double_to_Bool});
export const ModuleDouble = {l:[
{i: Double , t: Type },
{i: Double, t: Type},
{i: Double_to_Double_to_Double, t: Function },
{i: Double_to_Double_to_Bool , t: Function },
{i: Double_to_Double , t: Function },
{i: Double_to_Bool , t: Function },
{i: addDouble , t: Double_to_Double_to_Double },
{i: mulDouble , t: Double_to_Double_to_Double },
{i: eqDouble , t: Double_to_Double_to_Bool },
...typedFnType(addDouble, fnType => fnType(Double)(fnType(Double)(Double))),
...typedFnType(mulDouble, fnType => fnType(Double)(fnType(Double)(Double))),
...typedFnType(eqDouble, fnType => fnType(Double)(fnType(Double)(Bool))),
]};

View file

@ -1,30 +1,19 @@
import { fnType } from "../structures/function.js";
import { Type } from "../metacircular.js";
import { typedFnType } from "../structures/types.js";
import { Type } from "../type.js";
import {Bool, Int} from "./symbols.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 Int_to_Int = fnType({in: Int, out: Int });
const Int_to_Bool = fnType({in: Int, out: Bool});
export const Int_to_Int_to_Int = fnType({in: Int, out: Int_to_Int});
export const Int_to_Int_to_Bool = fnType({in: Int, out: Int_to_Bool});
const serialize = x => x.toString();
const deserialize = str => BigInt(str);
export const ModuleInt = {l:[
{i: Int , t: Type },
{i: Int_to_Int_to_Int , t: Function },
{i: Int_to_Int_to_Bool , t: Function },
{i: Int_to_Int , t: Function },
{i: Int_to_Bool , t: Function },
{i: addInt , t: Int_to_Int_to_Int },
{i: mulInt , t: Int_to_Int_to_Int },
{i: eqInt , t: Int_to_Int_to_Bool },
...typedFnType(addInt, fnType => fnType(Int)(fnType(Int)(Int))),
...typedFnType(mulInt, fnType => fnType(Int)(fnType(Int)(Int))),
...typedFnType(eqInt , fnType => fnType(Int)(fnType(Int)(Bool))),
]};

View file

@ -1,7 +1,8 @@
// to break up dependency cycles, primitive types are defined in their own JS module
export const Int = { symbol: Symbol('Int') , params: [] };
export const Bool = { symbol: Symbol('Bool') , params: [] };
export const Double = { symbol: Symbol('Double'), params: [] };
export const Byte = { symbol: Symbol('Byte') , params: [] };
export const Char = { symbol: Symbol('Char') , params: [] };
export const Char = { symbol: Symbol('Char') , params: [] };// Wrapper around function below.

29
stdlib.js Normal file
View file

@ -0,0 +1,29 @@
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 { 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 "./type.js";
import { ModuleTyped } from "./typed.js";
export const ModuleStd = {l:[
...ModuleType.l,
...ModuleTyped.l,
// Primitive types
...ModuleBool.l,
...ModuleByte.l,
...ModuleChar.l,
...ModuleDouble.l,
...ModuleInt.l,
// Types that consist of other types
...ModuleFunction.l,
...ModuleList.l,
...ModuleProduct.l,
...ModuleSum.l,
]};

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:[

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,7 +12,8 @@ const put = ls => i => elem => ({l: ls.l.with(Number(i), elem)});
export const ModuleList = {l:[
// Type -> Type
...typedFnType(lsType, fnType => fnType
...typedFnType(lsType, fnType =>
fnType
/* in */ (Type)
/* out */ (Type)
),
@ -31,7 +22,9 @@ export const ModuleList = {l:[
{i: emptyList, t: makeGeneric(a => lsType(a))},
// [a] -> Int -> a
...typedFnType(get, fnType => makeGeneric(a => fnType
...typedFnType(get, fnType =>
makeGeneric(a =>
fnType
/* in */ (lsType(a))
/* out */ (fnType
/* in */ (Int)
@ -39,7 +32,9 @@ export const ModuleList = {l:[
))),
// [a] -> Int -> a -> [a]
...typedFnType(put, fnType => makeGeneric(a => fnType
...typedFnType(put, fnType =>
makeGeneric(a =>
fnType
/* in */ (lsType(a))
/* out */ (fnType
/* in */ (Int)

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,7 +11,8 @@ const getRight = product => product.right;
export const ModuleProduct = {l: [
// binary type constructor
// Type -> Type -> Type
...typedFnType(prodType, fnType => fnType
...typedFnType(prodType, fnType =>
fnType
(Type)
(fnType
(Type)
@ -29,7 +21,9 @@ export const ModuleProduct = {l: [
),
// a -> b -> (a, b)
...typedFnType(constructor, fnType => makeGeneric((a, b) => fnType
...typedFnType(constructor, fnType =>
makeGeneric((a, b) =>
fnType
(a)
(fnType
(b)
@ -38,13 +32,17 @@ export const ModuleProduct = {l: [
)),
// (a, b) -> a
...typedFnType(getLeft, fnType => makeGeneric((a, b) => fnType
...typedFnType(getLeft, fnType =>
makeGeneric((a, b) =>
fnType
(prodType(a)(b))
(a)
)),
// (a, b) -> b
...typedFnType(getRight, fnType => makeGeneric((a, b) => fnType
...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,7 +16,8 @@ const match = sum => handlers => sum.variant === "L"
export const ModuleSum = {l:[
// binary type constructor
// Type -> Type -> Type
...typedFnType(sumType, fnType => fnType
...typedFnType(sumType, fnType =>
fnType
(Type)
(fnType
(Type)
@ -35,19 +26,25 @@ export const ModuleSum = {l:[
),
// a -> a | b
...typedFnType(constructorLeft, fnType => makeGeneric((a, b) => fnType
...typedFnType(constructorLeft, fnType =>
makeGeneric((a, b) =>
fnType
(a)
(sumType(a)(b))
)),
// b -> a | b
...typedFnType(constructorRight, fnType => makeGeneric((a, b) => fnType
...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
...typedFnType(match, fnType =>
makeGeneric((a, b, c) =>
fnType
(sumType(a)(b))
(fnType
(prodType

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);

View file

@ -1,5 +1,5 @@
import { Bool } from "./primitives/symbols.js";
import { typedFnType } from "./structures/function.js";
import { Bool } from "./primitives/types.js";
import { typedFnType } from "./structures/types.js";
import { deepEqual } from "./util.js";
// TODO: 'Type' (and its instances) are itself instances of (String,[Type]) (=the product type of String and list of Type)
@ -14,24 +14,26 @@ export const eqType = deepEqual;
// a module is just a set of typed objects
// each 'typed object' is implicitly an instance of TypeLink (defined below)
export const ModuleMetaCircular = {l:[
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
// Type : Type
// Type :: Type
{i: Type, t: Type},
// ...typedFnType(getSymbol, fnType => fnType({in: Type, out: Int})),
// ...typedFnType(getParams, fnType => fnType({in: Type, out: lsType(Type)})),
...typedFnType(eqType, fnType => fnType({
in: Type,
out: fnType({
in: Type,
out: Bool,
})})),
// Type -> Type -> Bool
...typedFnType(eqType, fnType =>
fnType
(Type)
(fnType
(Type)
(Bool)
)),
]};

View file

@ -1,7 +1,7 @@
import { makeGeneric } from "../generics/generics";
import { Type } from "../metacircular";
import { typedFnType } from "../structures/function";
import { Bool, Byte, Char, Double, Int } from "../primitives/symbols";
import { Type } from "../type";
import { typedFnType } from "../structures/types";
import { Bool, Byte, Char, Double, Int } from "../primitives/types";
import { deepEqual } from "../util";
import { eqDictType } from "./eq_type";

View file

@ -1,9 +1,9 @@
import { makeGeneric } from "../generics/generics.js";
import { addDouble, mulDouble } from "../primitives/double.js";
import { addInt, mulInt } from "../primitives/int.js";
import { Type } from "../metacircular.js";
import { typedFnType, typedFnType2 } from "../structures/function.js";
import { Double, Int } from "../primitives/symbols.js";
import { Type } from "../type.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;

View file

@ -1,7 +1,7 @@
import { assign } from "../generics/generics.js";
import { makeGeneric } from "../generics/generics.js";
import { fnType } from "../structures/function.js";
import { Double, Int } from "../primitives/symbols.js";
import { Double, Int } from "../primitives/types.js";
import { getMul, NumInstances } from "./num.js";
import { numDictType } from "./num_type.js";

View file

@ -1,5 +1,5 @@
import { fnType } from "./structures/function.js";
import { Type } from "./metacircular.js";
import { typedFnType } from "./structures/types.js";
import { Type } from "./type.js";
// Everything is (implicitly) typed by the Any type.
export const Any = { symbol: Symbol('Any'), params: [] };
@ -10,13 +10,9 @@ export const Typed = { symbol: Symbol('Typed'), params: [] };
const getInst = lnk => lnk.i;
const getType = lnk => lnk.t;
const Typed_to_Type = fnType({in: Any, out: Type});
export const ModuleTyped = {l:[
{i: Typed, t: Type},
{i: Typed_to_Type, t: Type},
{i: getInst, t: Typed_to_Type},
{i: getType, t: Typed_to_Type},
...typedFnType(getInst, fnType => fnType(Typed)(Type)),
...typedFnType(getType, fnType => fnType(Typed)(Type)),
]};