From 4ca60784aa2c0d41357d3560e139a77a2c465847 Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Thu, 20 Mar 2025 17:29:13 +0100 Subject: [PATCH] greatly simplify type registry --- lib/point.js | 8 ++-- type_registry.js | 111 ++++++++++++++--------------------------------- util.js | 2 +- 3 files changed, 36 insertions(+), 85 deletions(-) diff --git a/lib/point.js b/lib/point.js index 15d56f2..d02dd57 100644 --- a/lib/point.js +++ b/lib/point.js @@ -1,19 +1,17 @@ -import { Function, Type } from "../metacircular.js"; import { Double } from "../primitives/symbols.js"; -import { String } from "../structures/list_types/string.js"; import { nominalType, NominalType } from "../structures/nominal_type.js"; import { makeProductType } from "../structures/product.js"; -import { fnType, prodType, typedFnType } from "../type_registry.js"; +import { prodType, typedFnType } from "../type_registry.js"; const PointCartesian2D = nominalType( - // just a unique number, to make sure that our type is only equal to itself "PointCartesian2D") + // just a unique number, to make sure that our type is only equal to itself // BigInt("0xBBAAD62B10EE21993BA690A732DA2A6875CE4B6F5E7139D5AEC9FD887F9D24A8")) (prodType(Double, Double)); const PointPolar2D = nominalType( - // just a unique number, to make sure that our type is only equal to itself "PointPolar2D") + // just a unique number, to make sure that our type is only equal to itself // BigInt("0x31CDAB4B3D84C4EB27D3C111FD7580E533268B72E05BD694F8B262913E018B72")) (prodType(Double, Double)); diff --git a/type_registry.js b/type_registry.js index e2c6064..eb2ebb2 100644 --- a/type_registry.js +++ b/type_registry.js @@ -1,94 +1,47 @@ -// This module ensures that we never accidentally create a Symbol for the same type twice. -// Maybe we shouldn't use Symbols for types, but we also cannot use primitive values for types because they may accidentally overlap with 'real' values (that are not types) -// We also cannot use objects for types because similar to Symbols, objects are only equal to themselves if we use them as Map keys. +// This module ensures that we never accidentally create more than one JS object for the same type twice. +// We do so by creating (nested) DefaultMap(s) for all non-nullary type constructors +// It is a cheap workaround for JS lacking customizable hash-functions and equality-testing-functions. import { Function } from "./metacircular.js"; import { DefaultMap } from "./util.js"; -// Global store of function types: -const fnTypes = new DefaultMap(() => new Map()); -export const fnType = ({in: inType, out: outType}) => { - const m2 = fnTypes.getdefault(inType, true); - if (m2.has(outType)) { - return m2.get(outType); - } - else { - const fnType = {in: inType, out: outType}; - m2.set(outType, fnType); - return fnType; - } -}; +const listTypeRegistry = new DefaultMap(elementType => ({listOf: elementType})); +const genericTypeRegistry = new DefaultMap(underlyingType => ({generic: underlyingType})); +const fnTypeRegistry = new DefaultMap(inType => new DefaultMap(outType => ({in: inType, out: outType}))); +const productTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({operator: "product", leftType, rightType}))); +const sumTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({operator: "sum", leftType, rightType}))); + + +export const lsType = listTypeRegistry.getdefault.bind(listTypeRegistry); +export const genericType = genericTypeRegistry.getdefault.bind(genericTypeRegistry); +export const fnType = ({in: inType, out: outType}) => fnTypeRegistry.getdefault(inType, true).getdefault(outType, true); +export const sumType = (leftType, rightType) => sumTypeRegistry.getdefault(leftType, true).getdefault(rightType, true); +export const prodType = (leftType, rightType) => productTypeRegistry.getdefault(leftType, true).getdefault(rightType, 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}) => { + console.log('wrappedFnType called') const fnT = fnType({in: inType, out: outType}); fnTs.push(fnT); return fnT; } + const t = callback(wrappedFnType); // force evaluation return [ - {i: instance, t: callback(wrappedFnType)}, - ...fnTs.map(fnT => ({i: fnT, t: Function})), + t, + fnTs.map(fnT => ({i: fnT, t: Function})), ]; } - -// Global store of list types: -const listTypes = new Map(); -export const lsType = (elementType) => { - if (listTypes.has(elementType)) { - // only generate each list type once - // this would not be necessary if we could define our own equality and hash functions on objects in JavaScript. - return listTypes.get(elementType); - } - else { - const type = { - listOf: elementType, - }; - listTypes.set(elementType, type); - return type; - } -} - -// Global store of product types: -const productTypes = new DefaultMap(() => new Map()); -export const prodType = (leftType, rightType) => { - const m2 = productTypes.getdefault(leftType, true); - if (m2.has(rightType)) { - return m2.get(rightType); - } - else { - const pt = Symbol(`${leftType.toString()} × ${rightType.toString()}`); - m2.set(rightType, pt); - return pt; - } -} - -// Global store of sum types: -const sumTypes = new DefaultMap(() => new Map()); -export const sumType = (leftType, rightType) => { - const m2 = sumTypes.getdefault(leftType, true); - if (m2.has(rightType)) { - return m2.get(rightType); - } - else { - const st = Symbol(`${leftType.toString()} + ${rightType.toString()}`); - m2.set(rightType, st); - return st; - } -} - -// const genericTypes = new Map(); -// export const genericType = (underlyingType) => { -// if (genericTypes.has(underlyingType)) { -// // only generate each list type once -// // this would not be necessary if we could define our own equality and hash functions on objects in JavaScript. -// return genericTypes.get(underlyingType); -// } -// else { -// const type = { -// generic: underlyingType, -// }; -// genericTypes.set(underlyingType, type); -// return type; -// } -// } diff --git a/util.js b/util.js index 6cacf99..fa1aa4f 100644 --- a/util.js +++ b/util.js @@ -30,7 +30,7 @@ export class DefaultMap { } getdefault(key, addToMapping=false) { return this.m.get(key) || (() => { - const val = this.defaultValue(); + const val = this.defaultValue(key); if (addToMapping) this.m.set(key, val); return val;