// 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. 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; } }; // Global store of list types: const listTypes = new Map(); export function getListType(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 = Symbol('ListOf:' + elementType.toString()); 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; } }