type inferencing "unifying" operation is now bi-directional + begin writing generic version of "unifying" operation (that should work on all types)
This commit is contained in:
parent
c5ac55b0ff
commit
33c156fc5c
5 changed files with 148 additions and 9 deletions
|
|
@ -48,6 +48,75 @@ export const occurring = (type, typeVars) => {
|
|||
return new Set();
|
||||
}
|
||||
|
||||
export const properUnify = eqDict => (
|
||||
{typeVars: formalTypeVars, type: formalType},
|
||||
{typeVars: actualTypeVars, type: actualType},
|
||||
) => {
|
||||
if (getEq(eqDict)(formalType)(actualType)) {
|
||||
return {
|
||||
substitutions: new Map(),
|
||||
typeVars: new Set([
|
||||
...actualTypeVars,
|
||||
// ...formalTypeVars, // <- i don't think we need these?
|
||||
]),
|
||||
type: actualType,
|
||||
}
|
||||
}
|
||||
|
||||
if (formalTypeVars.has(formalType)) {
|
||||
// formalType is type variable -> substitute it by actualType
|
||||
return {
|
||||
substitutions: new Map([[formalType, actualType]]),
|
||||
typeVars: new Set([
|
||||
...actualTypeVars,
|
||||
...formalTypeVars,
|
||||
].filter(a => a !== formalType)),
|
||||
type: actualType,
|
||||
}
|
||||
}
|
||||
if (actualTypeVars.has(actualType)) {
|
||||
// same as above, but in opposite direction:
|
||||
// actualType is type variable -> substitute it by formalType
|
||||
return {
|
||||
substitutions: new Map([[actualType, formalType]]),
|
||||
typeVars: new Set([
|
||||
...actualTypeVars,
|
||||
...formalTypeVars,
|
||||
].filter(a => a !== actualType)),
|
||||
type: formalType,
|
||||
}
|
||||
}
|
||||
|
||||
// WIP...
|
||||
}
|
||||
|
||||
const mergeOneWay = (m1, m2) => {
|
||||
const m1copy = new Map(m1);
|
||||
const m2copy = new Map(m2);
|
||||
for (const [key1, val1] of m1copy.entries()) {
|
||||
if (m2copy.has(val1)) {
|
||||
m1copy.set(key1, m2.get(val1));
|
||||
m2copy.delete(val1);
|
||||
return [false, m1copy, m2copy, new Set([val1])];
|
||||
}
|
||||
}
|
||||
return [true, m1copy, m2copy, new Set()]; // stable
|
||||
}
|
||||
|
||||
export const mergeSubstitutions = (m1, m2) => {
|
||||
let stable = false;
|
||||
let deletedTypeVars = new Set();
|
||||
while (!stable) {
|
||||
let d;
|
||||
// notice we swap m2 and m1, so the rewriting can happen both ways:
|
||||
[stable, m2, m1, d] = mergeOneWay(m1, m2);
|
||||
if (!stable) {
|
||||
deletedTypeVars = deletedTypeVars.union(d);
|
||||
}
|
||||
}
|
||||
return [new Map([...m1, ...m2]), deletedTypeVars];
|
||||
}
|
||||
|
||||
// Currently very ad-hoc.
|
||||
|
||||
// Thanks to Hans for pointing out that this algorithm exactly like "Unification" in Prolog (hence the function name):
|
||||
|
|
@ -77,6 +146,17 @@ export const unify = (
|
|||
};
|
||||
}
|
||||
|
||||
if (actualTypeVars.has(actualType)) {
|
||||
// same as above, but in the other direction
|
||||
return {
|
||||
substitutions: new Map([[actualType, formalType]]),
|
||||
typeVars: new Set([
|
||||
...actualTypeVars,
|
||||
...formalTypeVars].filter(a => a !== actualType)),
|
||||
type: formalType,
|
||||
};
|
||||
}
|
||||
|
||||
if (formalType.in !== undefined) {
|
||||
// function type
|
||||
if (actualType.in === undefined) {
|
||||
|
|
@ -95,13 +175,15 @@ export const unify = (
|
|||
}
|
||||
}
|
||||
// merge substitutions
|
||||
const newSubstitutions = new Map([
|
||||
...inType.substitutions,
|
||||
...outType.substitutions,
|
||||
]);
|
||||
const [newSubstitutions, deletedTypeVars] = mergeSubstitutions(
|
||||
inType.substitutions, outType.substitutions);
|
||||
// const newSubstitutions = new Map([
|
||||
// ...inType.substitutions,
|
||||
// ...outType.substitutions,
|
||||
// ]);
|
||||
const newTypeVars = new Set([
|
||||
...actualTypeVars,
|
||||
...formalTypeVars].filter(a => !newSubstitutions.has(a)));
|
||||
...formalTypeVars].filter(a => !newSubstitutions.has(a) && !deletedTypeVars.has(a)));
|
||||
return {
|
||||
substitutions: newSubstitutions,
|
||||
typeVars: newTypeVars,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue