From 342d4b34efcac676064808336dc603b338601e8c Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Fri, 4 Apr 2025 10:04:40 +0200 Subject: [PATCH] fix bug in polymorphic types (when creating a tuple of two empty lists, there was only one type variable (the type of the list), whereas each list could have a different type, so there should be two type variables) + make type variables look nicer --- generics/generics.js | 37 +++++++++++++++++++++++++++++++------ structures/types.js | 2 +- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/generics/generics.js b/generics/generics.js index a634c33..93c0371 100644 --- a/generics/generics.js +++ b/generics/generics.js @@ -140,12 +140,14 @@ const unifyInternal = (typeVars, fType, aType) => { }; export const unify = (fGenericType, aGenericType) => { + let allTypeVars; + [allTypeVars, fGenericType, aGenericType] = safeUnionTypeVars(fGenericType, aGenericType); const {genericType} = unifyInternal( - fGenericType.typeVars.union(aGenericType.typeVars), + allTypeVars, fGenericType.type, aGenericType.type, ) - return genericType; + return recomputeTypeVars(genericType); } export const substitute = (type, substitutions) => { @@ -163,18 +165,41 @@ export const substitute = (type, substitutions) => { }; export const assign = (genFnType, paramType) => { + let allTypeVars; + [allTypeVars, genFnType, paramType] = safeUnionTypeVars(genFnType, paramType); const [inType, outType] = genFnType.type.params; - const allTypeVars = genFnType.typeVars.union(paramType.typeVars) const {substitutions} = unifyInternal(allTypeVars, inType, paramType.type); const substitutedOutType = substitute(outType, substitutions); - return onlyOccurring(substitutedOutType, allTypeVars); + return recomputeTypeVars(onlyOccurring(substitutedOutType, allTypeVars)); }; export const assignFn = (genFnType, paramType) => { + let allTypeVars; + [allTypeVars, genFnType, paramType] = safeUnionTypeVars(genFnType, paramType); const [inType] = genFnType.type.params; - const allTypeVars = genFnType.typeVars.union(paramType.typeVars) const {substitutions} = unifyInternal(allTypeVars, inType, paramType.type); // console.log({genFnType: prettyT(genFnType), paramType: prettyT(paramType), substitutions}) const substitutedFnType = substitute(genFnType.type, substitutions); - return onlyOccurring(substitutedFnType, allTypeVars); + return recomputeTypeVars(onlyOccurring(substitutedFnType, allTypeVars)); +} + +export const recomputeTypeVars = (genType) => { + const newTypeVars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'].map(Symbol); + let nextIdx = 0; + const subst = new Map(); + for (const typeVarA of genType.typeVars) { + subst.set(typeVarA, newTypeVars[nextIdx++]); + } + const substType = { + typeVars: new Set(subst.values()), + type: substitute(genType.type, subst), + }; + return substType; +} + +export const safeUnionTypeVars = (genTypeA, genTypeB) => { + const substTypeA = recomputeTypeVars(genTypeA); + const substTypeB = recomputeTypeVars(genTypeB); + const allTypeVars = substTypeA.typeVars.union(substTypeB.typeVars); + return [allTypeVars, substTypeA, substTypeB]; } \ No newline at end of file diff --git a/structures/types.js b/structures/types.js index d8b0427..d64d97b 100644 --- a/structures/types.js +++ b/structures/types.js @@ -62,7 +62,7 @@ export function prettyT(type) { } if (type.typeVars) { if (type.typeVars.size > 0) { - return `∀(${[...type.typeVars].map(prettyT).join(", ")}): ${prettyT(type.type)}`; + return `∀${[...type.typeVars].map(prettyT).sort((a,b)=>a.localeCompare(b)).join(",")}: ${prettyT(type.type)}`; } else { return prettyT(type.type);