From bc91d9bf390181ae6317b016051375f197cca4dc Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Sun, 23 Mar 2025 13:25:47 +0100 Subject: [PATCH] lotta progress --- generics/generics.js | 2 +- generics/generics.test.js | 4 +- interfaces/serializable.js | 16 ----- lib/id.js | 39 ------------ lib/point.js | 79 ++++++++++++----------- lib/square.js | 50 --------------- main.js | 98 +++++------------------------ primitives/bool.js | 20 +++--- primitives/byte.js | 14 ++--- primitives/char.js | 6 +- primitives/double.js | 25 +++----- primitives/int.js | 23 ++----- primitives/{symbols.js => types.js} | 3 +- stdlib.js | 29 +++++++++ structures/function.js | 44 ++----------- structures/list.js | 59 ++++++++--------- structures/list_types/module.js | 8 --- structures/list_types/string.js | 8 --- structures/nominal_type.js | 10 --- structures/product.js | 62 +++++++++--------- structures/sum.js | 71 ++++++++++----------- structures/types.js | 74 ++++++++++++++++++++++ metacircular.js => type.js | 22 ++++--- typeclasses/eq.js | 6 +- typeclasses/num.js | 6 +- typeclasses/num.test.js | 2 +- typed.js | 12 ++-- 27 files changed, 317 insertions(+), 475 deletions(-) delete mode 100644 interfaces/serializable.js delete mode 100644 lib/id.js delete mode 100644 lib/square.js rename primitives/{symbols.js => types.js} (77%) create mode 100644 stdlib.js delete mode 100644 structures/list_types/module.js delete mode 100644 structures/list_types/string.js delete mode 100644 structures/nominal_type.js create mode 100644 structures/types.js rename metacircular.js => type.js (76%) diff --git a/generics/generics.js b/generics/generics.js index f2ffb8c..7851964 100644 --- a/generics/generics.js +++ b/generics/generics.js @@ -1,4 +1,4 @@ -import { eqType } from "../metacircular.js"; +import { eqType } from "../type.js"; import { pretty, zip } from "../util.js"; // constructor for generic types diff --git a/generics/generics.test.js b/generics/generics.test.js index 40b4532..afb1dc7 100644 --- a/generics/generics.test.js +++ b/generics/generics.test.js @@ -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"; diff --git a/interfaces/serializable.js b/interfaces/serializable.js deleted file mode 100644 index 54498db..0000000 --- a/interfaces/serializable.js +++ /dev/null @@ -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}, -]}; diff --git a/lib/id.js b/lib/id.js deleted file mode 100644 index bc92227..0000000 --- a/lib/id.js +++ /dev/null @@ -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}, -// ]}; -// }; diff --git a/lib/point.js b/lib/point.js index 5efabb5..dc8231a 100644 --- a/lib/point.js +++ b/lib/point.js @@ -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 = dθ => ({left: r, right: θ}) => { - return {left: r, right: θ+dθ}; +export const rotate = dθ => ({r, θ}) => { + return {r, θ: θ+dθ}; } -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))), ]}; diff --git a/lib/square.js b/lib/square.js deleted file mode 100644 index 8720952..0000000 --- a/lib/square.js +++ /dev/null @@ -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}, -// ]}; diff --git a/main.js b/main.js index d6a6b73..b055154 100644 --- a/main.js +++ b/main.js @@ -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, ]}); diff --git a/primitives/bool.js b/primitives/bool.js index 9a5da56..b4bd3a0 100644 --- a/primitives/bool.js +++ b/primitives/bool.js @@ -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))), ]}; diff --git a/primitives/byte.js b/primitives/byte.js index 96ea66c..8e35a1b 100644 --- a/primitives/byte.js +++ b/primitives/byte.js @@ -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))), ]}; diff --git a/primitives/char.js b/primitives/char.js index 565b8d0..f84afc5 100644 --- a/primitives/char.js +++ b/primitives/char.js @@ -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; diff --git a/primitives/double.js b/primitives/double.js index e85e919..fc85ce2 100644 --- a/primitives/double.js +++ b/primitives/double.js @@ -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))), ]}; diff --git a/primitives/int.js b/primitives/int.js index c64d266..4b01c1a 100644 --- a/primitives/int.js +++ b/primitives/int.js @@ -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))), ]}; diff --git a/primitives/symbols.js b/primitives/types.js similarity index 77% rename from primitives/symbols.js rename to primitives/types.js index adb5d3e..bb9f0b0 100644 --- a/primitives/symbols.js +++ b/primitives/types.js @@ -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. diff --git a/stdlib.js b/stdlib.js new file mode 100644 index 0000000..e25b914 --- /dev/null +++ b/stdlib.js @@ -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, +]}; \ No newline at end of file diff --git a/structures/function.js b/structures/function.js index 95416d8..00a93f2 100644 --- a/structures/function.js +++ b/structures/function.js @@ -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) ) ), -]}; \ No newline at end of file +]}; diff --git a/structures/list.js b/structures/list.js index 2b8f3a5..8cffbf2 100644 --- a/structures/list.js +++ b/structures/list.js @@ -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)) + ) + ))), ]}; diff --git a/structures/list_types/module.js b/structures/list_types/module.js deleted file mode 100644 index a2c72bf..0000000 --- a/structures/list_types/module.js +++ /dev/null @@ -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 diff --git a/structures/list_types/string.js b/structures/list_types/string.js deleted file mode 100644 index a17465a..0000000 --- a/structures/list_types/string.js +++ /dev/null @@ -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); \ No newline at end of file diff --git a/structures/nominal_type.js b/structures/nominal_type.js deleted file mode 100644 index 9785af0..0000000 --- a/structures/nominal_type.js +++ /dev/null @@ -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!! diff --git a/structures/product.js b/structures/product.js index 6d5e20e..2718cc7 100644 --- a/structures/product.js +++ b/structures/product.js @@ -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) + )), ]}; diff --git a/structures/sum.js b/structures/sum.js index 62a5e90..c41ef46 100644 --- a/structures/sum.js +++ b/structures/sum.js @@ -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) + ) + )), ]}; diff --git a/structures/types.js b/structures/types.js new file mode 100644 index 0000000..bbff7b4 --- /dev/null +++ b/structures/types.js @@ -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); diff --git a/metacircular.js b/type.js similarity index 76% rename from metacircular.js rename to type.js index 0779d44..7528535 100644 --- a/metacircular.js +++ b/type.js @@ -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) + )), ]}; diff --git a/typeclasses/eq.js b/typeclasses/eq.js index 12c140d..4b9482f 100644 --- a/typeclasses/eq.js +++ b/typeclasses/eq.js @@ -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"; diff --git a/typeclasses/num.js b/typeclasses/num.js index 683120a..2418071 100644 --- a/typeclasses/num.js +++ b/typeclasses/num.js @@ -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; diff --git a/typeclasses/num.test.js b/typeclasses/num.test.js index 16405f9..a310c07 100644 --- a/typeclasses/num.test.js +++ b/typeclasses/num.test.js @@ -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"; diff --git a/typed.js b/typed.js index f3deebe..843a731 100644 --- a/typed.js +++ b/typed.js @@ -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)), ]};