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"; import { pretty, zip } from "../util.js";
// constructor for generic types // constructor for generic types

View file

@ -1,5 +1,5 @@
import { Bool, Int } from "../primitives/symbols.js"; import { Bool, Int } from "../primitives/types.js";
import { lsType } from "../structures/list.js"; import { lsType } from "../structures/types.js";
import { fnType } from "../structures/function.js"; import { fnType } from "../structures/function.js";
import { assign, makeGeneric, unify } from "./generics.js"; import { assign, makeGeneric, unify } from "./generics.js";
import { pretty } from "../util.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 { Type } from "../type.js";
import { nominalType, NominalType } from "../structures/nominal_type.js"; import { typedFnType } from "../structures/types.js"
import { makeProductType } from "../structures/product.js"; import { Double } from "../primitives/types.js";
import { typedFnType } from "../metacircular.js";
import { prodType } from "../structures/product.js";
const PointCartesian2D = nominalType( // Create nominal types
"PointCartesian2D") export const PointCartesian2D = { symbol: Symbol('PointCartesian2D'), params: [] };
// just a unique number, to make sure that our type is only equal to itself export const PointPolar2D = { symbol: Symbol('PointPolar2D') , params: [] };
// BigInt("0xBBAAD62B10EE21993BA690A732DA2A6875CE4B6F5E7139D5AEC9FD887F9D24A8"))
(prodType(Double, Double));
const PointPolar2D = nominalType( export const cart2polar = ({x, y}) => {
"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}) => {
const r = Math.sqrt(x*x + y*y); const r = Math.sqrt(x*x + y*y);
const θ = Math.atan(y/x); 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 x = r * Math.cos(θ);
const y = r * Math.sin(θ); 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}; return {left: x+dx, right: y+dy};
} }
export const rotate = => ({left: r, right: θ}) => { export const rotate = => ({r, θ}) => {
return {left: r, right: θ+}; return {r, θ: θ+};
} }
export const scale = dr => ({left: r, right: θ}) => { export const scale = dr => ({r, θ}) => {
return {left: r+dr, right: θ}; return {r: r+dr, θ};
} }
const examplePoint = {left: 1, right: 2}; const examplePoint = {x: 1, y: 2};
export const ModulePoint = {l:[ export const ModulePoint = {l:[
{i: -1 , t: Double}, {i: -1 , t: Double},
{i: 2 , t: Double}, {i: 2 , t: Double},
{i: examplePoint , t: PointCartesian2D}, {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}, ...typedFnType(cart2polar, fnType => fnType(PointCartesian2D)(PointPolar2D)),
{i: PointPolar2D , t: NominalType},
...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})})), // Double -> PointPolar2D -> PointPolar2D
...typedFnType(scale, fnType =>
...typedFnType(scale, fnType => fnType({in: Double, out: fnType({in: PointPolar2D, out: PointPolar2D})})), 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 { select } from '@inquirer/prompts';
import {ModuleTyped} from "./typed.js"; import { ModulePoint } from "./lib/point.js";
import {ModuleBool} from "./primitives/bool.js"; import { DefaultMap, pretty } from './util.js';
import { Double_to_Double_to_Double, ModuleDouble, mulDouble } from "./primitives/double.js"; import { symbolFunction } from './structures/types.js';
import {Int_to_Int_to_Int, ModuleInt, mulInt} from "./primitives/int.js"; import { ModuleStd } from './stdlib.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";
class Context { class Context {
constructor(mod) { constructor(mod) {
this.functionsFrom = new DefaultMap(() => []); this.functionsFrom = new DefaultMap(() => []); // type to outgoing function
this.functionsTo = new DefaultMap(() => []); this.functionsTo = new DefaultMap(() => []); // type to incoming function
this.types = new DefaultMap(() => []); this.types = new DefaultMap(() => []); // instance to type
this.instances = new DefaultMap(() => []); this.instances = new DefaultMap(() => []); // type to instance
for (const {i, t} of mod.l) { for (const {i, t} of mod.l) {
this.types.getdefault(i, true).push(t); this.types.getdefault(i, true).push(t);
this.instances.getdefault(t, true).push(i); this.instances.getdefault(t, true).push(i);
} }
for (const fnType of this.instances.getdefault(Function)) { for (const [i, types] of this.instances.m.entries()) {
for (const fn of this.instances.getdefault(fnType)) { for (const t of types) {
this.functionsFrom.getdefault(getIn(fnType), true).push(fn); if (t.symbol === symbolFunction) {
this.functionsTo.getdefault(getOut(fnType), true).push(fn); // '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:[ const ctx = new Context({l:[
// ...ModuleMetaCircular.l, ...ModuleStd.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,
...ModulePoint.l, ...ModulePoint.l,
]}); ]});

View file

@ -1,15 +1,15 @@
import { fnType } from "../structures/function.js"; import { fnType, typedFnType } from "../structures/types.js";
import { Type } from "../metacircular.js"; import { Type } from "../type.js";
import { Bool } from "./symbols.js"; import { Bool } from "./types.js";
const eqBool = x => y => x === y; 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:[ export const ModuleBool = {l:[
{i: Bool , t: Type }, {i: true , t: Bool},
{i: Bool_to_Bool , t: Function }, {i: false, t: Bool},
{i: Bool_to_Bool_to_Bool , t: Function },
{i: eqBool , t: Bool_to_Bool_to_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 { typedFnType } from "../structures/types.js";
import { Type } from "../metacircular.js"; import { Type } from "../type.js";
import {Byte, Bool} from "./symbols.js"; import {Byte, Bool} from "./types.js";
const eqByte = x => y => x === y; 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:[ export const ModuleByte = {l:[
{i: Byte , t: Type }, {i: Byte , t: Type },
{i: Byte_to_Bool , t: Function },
{i: Byte_to_Byte_to_Bool , t: Function }, ...typedFnType(eqByte, fnType => fnType(Byte)(fnType(Byte)(Bool))),
{i: eqByte , t: Byte_to_Byte_to_Bool },
]}; ]};

View file

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

View file

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

View file

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

View file

@ -1,7 +1,8 @@
// to break up dependency cycles, primitive types are defined in their own JS module // to break up dependency cycles, primitive types are defined in their own JS module
export const Int = { symbol: Symbol('Int') , params: [] }; export const Int = { symbol: Symbol('Int') , params: [] };
export const Bool = { symbol: Symbol('Bool') , params: [] }; export const Bool = { symbol: Symbol('Bool') , params: [] };
export const Double = { symbol: Symbol('Double'), params: [] }; export const Double = { symbol: Symbol('Double'), params: [] };
export const Byte = { symbol: Symbol('Byte') , 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"; import { Type } from "../type.js";
import { typedFnType } from "./types.js";
const symbolFunction = Symbol('Function'); import { fnType } from "./types.js";
// 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 })),
];
};
export const ModuleFunction = {l:[ export const ModuleFunction = {l:[
@ -48,4 +12,4 @@ export const ModuleFunction = {l:[
/* out */ (Type) /* out */ (Type)
) )
), ),
]}; ]};

View file

@ -1,18 +1,8 @@
import { typedFnType } from "./function.js"; import { typedFnType } from "./types.js";
import { Type } from "../metacircular.js"; import { Type } from "../type.js";
import { Int } from "../primitives/symbols.js"; import { Int } from "../primitives/types.js";
import { DefaultMap } from "../util.js";
import { makeGeneric } from "../generics/generics.js"; import { makeGeneric } from "../generics/generics.js";
import { lsType } from "./types.js";
const symbolList = Symbol('List');
const listTypeRegistry = new DefaultMap(elementType => ({
symbol: symbolList,
params: [elementType],
}));
// type constructor
export const lsType = elementType => listTypeRegistry.getdefault(elementType, true);
// 'normal' implementation // 'normal' implementation
const emptyList = {l:[]}; const emptyList = {l:[]};
@ -22,30 +12,35 @@ const put = ls => i => elem => ({l: ls.l.with(Number(i), elem)});
export const ModuleList = {l:[ export const ModuleList = {l:[
// Type -> Type // Type -> Type
...typedFnType(lsType, fnType => fnType ...typedFnType(lsType, fnType =>
/* in */ (Type) fnType
/* out */ (Type) /* in */ (Type)
/* out */ (Type)
), ),
// [a] // [a]
{i: emptyList, t: makeGeneric(a => lsType(a))}, {i: emptyList, t: makeGeneric(a => lsType(a))},
// [a] -> Int -> a // [a] -> Int -> a
...typedFnType(get, fnType => makeGeneric(a => fnType ...typedFnType(get, fnType =>
/* in */ (lsType(a)) makeGeneric(a =>
/* out */ (fnType fnType
/* in */ (Int) /* in */ (lsType(a))
/* out */ (a) /* out */ (fnType
))), /* in */ (Int)
/* out */ (a)
))),
// [a] -> Int -> a -> [a] // [a] -> Int -> a -> [a]
...typedFnType(put, fnType => makeGeneric(a => fnType ...typedFnType(put, fnType =>
/* in */ (lsType(a)) makeGeneric(a =>
/* out */ (fnType fnType
/* in */ (Int) /* in */ (lsType(a))
/* out */ (fnType /* out */ (fnType
/* in */ (a) /* in */ (Int)
/* out */ (lsType(a)) /* 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 { makeGeneric } from "../generics/generics.js";
import { Type } from "../metacircular.js"; import { Type } from "../type.js";
import { DefaultMap } from "../util.js"; import { typedFnType } from "./types.js";
import { typedFnType } from "./function.js"; import { prodType } from "./types.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);
// In JS, all products are encoded in the same way: // In JS, all products are encoded in the same way:
const constructor = left => right => ({left, right}); const constructor = left => right => ({left, right});
@ -20,32 +11,39 @@ const getRight = product => product.right;
export const ModuleProduct = {l: [ export const ModuleProduct = {l: [
// binary type constructor // binary type constructor
// Type -> Type -> Type // Type -> Type -> Type
...typedFnType(prodType, fnType => fnType ...typedFnType(prodType, fnType =>
(Type) fnType
(fnType
(Type) (Type)
(Type) (fnType
) (Type)
(Type)
)
), ),
// a -> b -> (a, b) // a -> b -> (a, b)
...typedFnType(constructor, fnType => makeGeneric((a, b) => fnType ...typedFnType(constructor, fnType =>
(a) makeGeneric((a, b) =>
(fnType fnType
(b) (a)
(prodType(a)(b)) (fnType
) (b)
)), (prodType(a)(b))
)
)),
// (a, b) -> a // (a, b) -> a
...typedFnType(getLeft, fnType => makeGeneric((a, b) => fnType ...typedFnType(getLeft, fnType =>
(prodType(a)(b)) makeGeneric((a, b) =>
(a) fnType
)), (prodType(a)(b))
(a)
)),
// (a, b) -> b // (a, b) -> b
...typedFnType(getRight, fnType => makeGeneric((a, b) => fnType ...typedFnType(getRight, fnType =>
(prodType(a)(b)) makeGeneric((a, b) =>
(b) fnType
)), (prodType(a)(b))
(b)
)),
]}; ]};

View file

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

View file

@ -1,5 +1,5 @@
import { Bool } from "./primitives/symbols.js"; import { Bool } from "./primitives/types.js";
import { typedFnType } from "./structures/function.js"; import { typedFnType } from "./structures/types.js";
import { deepEqual } from "./util.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) // 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 // a module is just a set of typed objects
// each 'typed object' is implicitly an instance of TypeLink (defined below) // each 'typed object' is implicitly an instance of TypeLink (defined below)
export const ModuleMetaCircular = {l:[ export const ModuleType = {l:[
// TODO? maybe follow Lean so // TODO? maybe follow Lean so
// Type.{0} : Type.{1} // Type.{0} : Type.{1}
// Type.{1} : Type.{2} // Type.{1} : Type.{2}
// ... // ...
// see: https://lean-lang.org/functional_programming_in_lean/functor-applicative-monad/universes.html // see: https://lean-lang.org/functional_programming_in_lean/functor-applicative-monad/universes.html
// Type : Type // Type :: Type
{i: Type, t: Type}, {i: Type, t: Type},
// ...typedFnType(getSymbol, fnType => fnType({in: Type, out: Int})), // ...typedFnType(getSymbol, fnType => fnType({in: Type, out: Int})),
// ...typedFnType(getParams, fnType => fnType({in: Type, out: lsType(Type)})), // ...typedFnType(getParams, fnType => fnType({in: Type, out: lsType(Type)})),
...typedFnType(eqType, fnType => fnType({ // Type -> Type -> Bool
in: Type, ...typedFnType(eqType, fnType =>
out: fnType({ fnType
in: Type, (Type)
out: Bool, (fnType
})})), (Type)
(Bool)
)),
]}; ]};

View file

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

View file

@ -1,9 +1,9 @@
import { makeGeneric } from "../generics/generics.js"; import { makeGeneric } from "../generics/generics.js";
import { addDouble, mulDouble } from "../primitives/double.js"; import { addDouble, mulDouble } from "../primitives/double.js";
import { addInt, mulInt } from "../primitives/int.js"; import { addInt, mulInt } from "../primitives/int.js";
import { Type } from "../metacircular.js"; import { Type } from "../type.js";
import { typedFnType, typedFnType2 } from "../structures/function.js"; import { typedFnType, typedFnType2 } from "../structures/types.js";
import { Double, Int } from "../primitives/symbols.js"; import { Double, Int } from "../primitives/types.js";
import { numDictType } from "./num_type.js"; import { numDictType } from "./num_type.js";
export const getAdd = numDict => numDict.add; export const getAdd = numDict => numDict.add;

View file

@ -1,7 +1,7 @@
import { assign } from "../generics/generics.js"; import { assign } from "../generics/generics.js";
import { makeGeneric } from "../generics/generics.js"; import { makeGeneric } from "../generics/generics.js";
import { fnType } from "../structures/function.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 { getMul, NumInstances } from "./num.js";
import { numDictType } from "./num_type.js"; import { numDictType } from "./num_type.js";

View file

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