import { inspect } from "node:util"; import { inspectType } from "../meta/type_constructor.js"; import { getSymbol } from "../primitives/type.js"; import { isTypeVar, TYPE_VARS, UNBOUND_SYMBOLS } from "../primitives/typevars.js"; // helper for creating generic types // for instance, the type: // a -> a -> Bool // is created by // makeGeneric(a => fnType(() => a)(() => fnType(() => a)(() => Bool))) export const makeGeneric = callback => { // type variables to make available: const type = callback(...TYPE_VARS); return type; }; const _occurring = stack => type => { if (isTypeVar(type)) { return new Set([getSymbol(type)]); } const tag = stack.length; const newStack = [...stack, tag]; return new Set(type.params.flatMap(p => { const innerType = p(tag); if (newStack.includes(innerType)) { return []; // no endless recursion! } return [..._occurring(newStack)(innerType)]; })); }; // Get set of type variables in type. export const occurring = _occurring([]); export const substitute = (type, substitutions, stack=[]) => { // console.log('substitute...', {type, substitutions, stack}); const found = substitutions.get(getSymbol(type)); if (found) { return found; } return { symbol: getSymbol(type), params: type.params.map(getParam => parent => { const param = getParam(parent); if (stack.includes(param)) { // param points back up - that's ok - means we don't have to recurse return param; } return substitute(param, substitutions, [...stack, parent]); }), [inspect.custom]: inspectType, }; }; // Ensures that no type variables overlap export const recomputeTypeVars = (types, skip=0) => { return recomputeTypeVarsWithInverse(types, skip)[0]; }; export const recomputeTypeVarsWithInverse = (types, skip=0) => { let nextIdx = skip; const inverse = new Map(); return [types.map(type => { const substitutions = new Map(); const typeVars = occurring(type); for (const typeVar of typeVars) { const idx = nextIdx++; if (typeVar !== UNBOUND_SYMBOLS[idx]) { substitutions.set(typeVar, TYPE_VARS[idx]); inverse.set(UNBOUND_SYMBOLS[idx], typeVar); } } return substitute(type, substitutions); }), inverse]; }