Compare commits
2 commits
68bd7cdb9f
...
3d72f4d416
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d72f4d416 | |||
| 2333abe70f |
7 changed files with 62 additions and 96 deletions
25
index.d.ts
vendored
25
index.d.ts
vendored
|
|
@ -165,16 +165,13 @@ export const unit: {
|
||||||
|
|
||||||
export function RBTreeWrapper(...args: any[]): any;
|
export function RBTreeWrapper(...args: any[]): any;
|
||||||
|
|
||||||
export class UnifyError extends Error {};
|
export class IncompatibleTypesError extends Error {};
|
||||||
|
export class SubstitutionCycle extends Error {};
|
||||||
export class NotAFunctionError extends Error {};
|
|
||||||
|
|
||||||
export function addDouble(x: any): any;
|
export function addDouble(x: any): any;
|
||||||
|
|
||||||
export function addInt(x: any): any;
|
export function addInt(x: any): any;
|
||||||
|
|
||||||
export function apply(input: any): any;
|
|
||||||
|
|
||||||
export function assignFn(funType: any, paramType: any): any;
|
export function assignFn(funType: any, paramType: any): any;
|
||||||
|
|
||||||
export function assignFnSubstitutions(funType: any, paramType: any, skip?: number): [any, any, any, any];
|
export function assignFnSubstitutions(funType: any, paramType: any, skip?: number): [any, any, any, any];
|
||||||
|
|
@ -243,8 +240,6 @@ export function getCompatibleInputTypes(env: any): any;
|
||||||
|
|
||||||
export function getDefaultTypeParser(): any;
|
export function getDefaultTypeParser(): any;
|
||||||
|
|
||||||
export function getEnabledFunctions(env: any): any;
|
|
||||||
|
|
||||||
export function getFunctions(env: any): any;
|
export function getFunctions(env: any): any;
|
||||||
|
|
||||||
export function getHumanReadableName(symbol: any): any;
|
export function getHumanReadableName(symbol: any): any;
|
||||||
|
|
@ -334,9 +329,9 @@ export function pretty(obj: any): any;
|
||||||
|
|
||||||
export function prettyT(type: any): any;
|
export function prettyT(type: any): any;
|
||||||
|
|
||||||
export function prettyU(unification: any): string;
|
export function prettyS(typevar: any, type: any): string;
|
||||||
|
|
||||||
export function prettyST(setOfTypes: any): string;
|
export function prettySS(substitution: Map<string, any>): string;
|
||||||
|
|
||||||
export function prodType(typeParam: any): any;
|
export function prodType(typeParam: any): any;
|
||||||
|
|
||||||
|
|
@ -350,17 +345,15 @@ export function structType(fields: any, rootSelf: any): any;
|
||||||
|
|
||||||
export function substitute(type: any, substitutions: any, stack: any): any;
|
export function substitute(type: any, substitutions: any, stack: any): any;
|
||||||
|
|
||||||
|
export function subsitutionsEqual(m1: any, m2: any): boolean;
|
||||||
|
|
||||||
|
export function mergeSubstitutionsN(subs: any): any;
|
||||||
|
|
||||||
export function sumType(typeParam: any): any;
|
export function sumType(typeParam: any): any;
|
||||||
|
|
||||||
export function transitivelyGrow(uni: any): any;
|
export function transitivelyGrow(uni: any): any;
|
||||||
|
|
||||||
export function unify(fType: any, aType: any): any;
|
export function unify(typeA, typeB): any;
|
||||||
|
|
||||||
export function unifyLL(typeA: any, typeB: any): any;
|
|
||||||
|
|
||||||
export function mergeUnifications(uniA: any, uniB: any): any;
|
|
||||||
|
|
||||||
export function reduceUnification(uni: any): any;
|
|
||||||
|
|
||||||
export function zip(a: any, b: any): any;
|
export function zip(a: any, b: any): any;
|
||||||
|
|
||||||
|
|
|
||||||
2
index.js
2
index.js
|
|
@ -16,7 +16,7 @@ export * from "./lib/structures/enum.types.js";
|
||||||
export * from "./lib/structures/product.js";
|
export * from "./lib/structures/product.js";
|
||||||
export * from "./lib/parser/type_parser.js";
|
export * from "./lib/parser/type_parser.js";
|
||||||
export * from "./lib/generics/generics.js";
|
export * from "./lib/generics/generics.js";
|
||||||
export * from "./lib/generics/low_level.js";
|
export * from "./lib/generics/unify.js";
|
||||||
export * from "./lib/stdlib.js";
|
export * from "./lib/stdlib.js";
|
||||||
export * from "./lib/compare/type.js";
|
export * from "./lib/compare/type.js";
|
||||||
export * from "./lib/compare/dynamic.js";
|
export * from "./lib/compare/dynamic.js";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { makeCompareFn } from "../compare/dynamic.js";
|
import { makeCompareFn } from "../compare/dynamic.js";
|
||||||
import { compareTypes } from "../compare/type.js";
|
import { compareTypes } from "../compare/type.js";
|
||||||
import { assignFn, UnifyError } from "../generics/generics.js";
|
|
||||||
import { getInst, getType, newDynamic } from "../primitives/dynamic.js";
|
import { getInst, getType, newDynamic } from "../primitives/dynamic.js";
|
||||||
import { Dynamic, Type } from "../primitives/primitive_types.js";
|
import { Dynamic, Type } from "../primitives/primitive_types.js";
|
||||||
import { getSymbol } from "../primitives/type.js";
|
import { getSymbol } from "../primitives/type.js";
|
||||||
|
|
@ -77,22 +76,22 @@ export const getFunctions = env => {
|
||||||
})([])(types);
|
})([])(types);
|
||||||
};
|
};
|
||||||
|
|
||||||
// return list of functions that can be called on 'dynamic'
|
// // return list of functions that can be called on 'dynamic'
|
||||||
export const getEnabledFunctions = env => dynamic => {
|
// export const getEnabledFunctions = env => dynamic => {
|
||||||
const allFunctions = getFunctions(env);
|
// const allFunctions = getFunctions(env);
|
||||||
const enabled = foldList(enabled => fun => {
|
// const enabled = foldList(enabled => fun => {
|
||||||
try {
|
// try {
|
||||||
const outType = assignFn(getType(fun), getType(dynamic));
|
// const outType = assignFn(getType(fun), getType(dynamic));
|
||||||
return [...enabled, {fun, outType}];
|
// return [...enabled, {fun, outType}];
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
if (!(e instanceof UnifyError)) {
|
// if (!(e instanceof UnifyError)) {
|
||||||
throw e;
|
// throw e;
|
||||||
}
|
// }
|
||||||
return enabled;
|
// return enabled;
|
||||||
}
|
// }
|
||||||
})([])(allFunctions);
|
// })([])(allFunctions);
|
||||||
return enabled;
|
// return enabled;
|
||||||
};
|
// };
|
||||||
|
|
||||||
export const getCompatibleInputTypes = env => funType => {
|
export const getCompatibleInputTypes = env => funType => {
|
||||||
const allTypes = getTypes(env);
|
const allTypes = getTypes(env);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
import { inspectType, makeTypeConstructor } from "../meta/type_constructor.js";
|
import { inspectType } from "../meta/type_constructor.js";
|
||||||
import { getSymbol } from "../primitives/type.js";
|
import { getSymbol } from "../primitives/type.js";
|
||||||
import { isTypeVar, TYPE_VARS, UNBOUND_SYMBOLS } from "../primitives/typevars.js";
|
import { isTypeVar, TYPE_VARS, UNBOUND_SYMBOLS } from "../primitives/typevars.js";
|
||||||
import { symbolFunction } from "../structures/type_constructors.js";
|
|
||||||
import { prettyT } from '../util/pretty.js';
|
|
||||||
import { reduceUnification, unifyLL } from "./low_level.js";
|
|
||||||
|
|
||||||
// helper for creating generic types
|
// helper for creating generic types
|
||||||
// for instance, the type:
|
// for instance, the type:
|
||||||
|
|
@ -35,18 +32,6 @@ const _occurring = stack => type => {
|
||||||
// Get set of type variables in type.
|
// Get set of type variables in type.
|
||||||
export const occurring = _occurring([]);
|
export const occurring = _occurring([]);
|
||||||
|
|
||||||
export class UnifyError extends Error {}
|
|
||||||
export class NotAFunctionError extends Error {}
|
|
||||||
|
|
||||||
export const unify = (fType, aType) => {
|
|
||||||
[fType, aType] = recomputeTypeVars([fType, aType]);
|
|
||||||
const unification = unifyLL(fType, aType);
|
|
||||||
const substitutions = reduceUnification(unification);
|
|
||||||
const uType = substitute(fType, // or aType, doesn't matter here
|
|
||||||
substitutions);
|
|
||||||
return recomputeTypeVars([uType])[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const substitute = (type, substitutions, stack=[]) => {
|
export const substitute = (type, substitutions, stack=[]) => {
|
||||||
// console.log('substitute...', {type, substitutions, stack});
|
// console.log('substitute...', {type, substitutions, stack});
|
||||||
const found = substitutions.get(getSymbol(type));
|
const found = substitutions.get(getSymbol(type));
|
||||||
|
|
@ -67,30 +52,6 @@ export const substitute = (type, substitutions, stack=[]) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const assignFn = (funType, paramType, skip=0) => {
|
|
||||||
// Precondition
|
|
||||||
if (getSymbol(funType) !== symbolFunction) {
|
|
||||||
throw new NotAFunctionError(`${prettyT(funType)} is not a function type!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Very important: Function and parameter type may have overlapping type variables, so we recompute them to make them non-overlapping:
|
|
||||||
const [funType1, paramType1] = recomputeTypeVars([funType, paramType]);
|
|
||||||
|
|
||||||
// Step 2: Get input and output type of function
|
|
||||||
const [inType1, outType1] = funType1.params.map(p => p(funType1));
|
|
||||||
|
|
||||||
// Step 3: Unify parameter type with input type
|
|
||||||
const unifInType1 = unifyLL(inType1, paramType1);
|
|
||||||
|
|
||||||
// Step 4: Substitute typevars in output type
|
|
||||||
const substInType1 = reduceUnification(unifInType1);
|
|
||||||
const reducedOutType1 = substitute(outType1, substInType1);
|
|
||||||
|
|
||||||
// Step 5: 'Normalize' output type
|
|
||||||
const [outType] = recomputeTypeVars([reducedOutType1], skip);
|
|
||||||
return outType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensures that no type variables overlap
|
// Ensures that no type variables overlap
|
||||||
export const recomputeTypeVars = (types, skip=0) => {
|
export const recomputeTypeVars = (types, skip=0) => {
|
||||||
return recomputeTypeVarsWithInverse(types, skip)[0];
|
return recomputeTypeVarsWithInverse(types, skip)[0];
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export class SubstitutionCycle extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pure
|
||||||
export const subsitutionsEqual = (m1,m2) => {
|
export const subsitutionsEqual = (m1,m2) => {
|
||||||
if (m1.size !== m2.size ) return false;
|
if (m1.size !== m2.size ) return false;
|
||||||
for (const [key1,type1] of m1) {
|
for (const [key1,type1] of m1) {
|
||||||
|
|
@ -37,6 +38,7 @@ export const subsitutionsEqual = (m1,m2) => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// pure
|
||||||
// Partial ordering between types
|
// Partial ordering between types
|
||||||
// - deep-equal types are equal (e.g., Int == Int)
|
// - deep-equal types are equal (e.g., Int == Int)
|
||||||
// - non-typevars are smaller than typevars (e.g., Int < a)
|
// - non-typevars are smaller than typevars (e.g., Int < a)
|
||||||
|
|
@ -68,17 +70,20 @@ const partialCompareTypes = (typeA, typeB) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pure
|
||||||
const checkCycle = (typevar, type) => {
|
const checkCycle = (typevar, type) => {
|
||||||
if (occurring(type).has(typevar)) {
|
if (occurring(type).has(typevar)) {
|
||||||
throw new SubstitutionCycle(typevar, type);
|
throw new SubstitutionCycle(typevar, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// impure, modifies 'substitutions'
|
||||||
const addReduce = (substitutions, typevar, type) => {
|
const addReduce = (substitutions, typevar, type) => {
|
||||||
// console.log('add ', prettyS(typevar, type));
|
// console.log('add ', prettyS(typevar, type));
|
||||||
substitutions.set(typevar, type);
|
substitutions.set(typevar, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// impure, modifies 'substitutions'
|
||||||
const attemptReduce = (substitutions, typevar, type) => {
|
const attemptReduce = (substitutions, typevar, type) => {
|
||||||
// assuming 'substitutions' is already reduced as much as possible,
|
// assuming 'substitutions' is already reduced as much as possible,
|
||||||
// substitute all typevars in our type with the existing substitutions
|
// substitute all typevars in our type with the existing substitutions
|
||||||
|
|
@ -108,8 +113,24 @@ const attemptReduce = (substitutions, typevar, type) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pure
|
||||||
|
export const mergeSubstitutionsN = (substs) => {
|
||||||
|
return substs.reduce((acc, cur) => {
|
||||||
|
return mergeSubstitutions2(acc, cur);
|
||||||
|
}, new Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
// pure
|
||||||
|
const mergeSubstitutions2 = (substA, substB) => {
|
||||||
|
const result = new Map(substA);
|
||||||
|
for (const [typevarB, typeB] of substB) {
|
||||||
|
attemptReduce(result, typevarB, typeB);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pure
|
||||||
export const unify = (typeA, typeB) => {
|
export const unify = (typeA, typeB) => {
|
||||||
const substitutions = new Map();
|
|
||||||
try {
|
try {
|
||||||
if (isTypeVar(typeA)) {
|
if (isTypeVar(typeA)) {
|
||||||
if (isTypeVar(typeB)) {
|
if (isTypeVar(typeB)) {
|
||||||
|
|
@ -139,20 +160,14 @@ export const unify = (typeA, typeB) => {
|
||||||
throw new IncompabibleTypesError(typeA, typeB);
|
throw new IncompabibleTypesError(typeA, typeB);
|
||||||
}
|
}
|
||||||
|
|
||||||
const unifiedParams = zip(typeA.params, typeB.params)
|
const subs = zip(typeA.params, typeB.params)
|
||||||
.map(([getParamA, getParamB]) => {
|
.map(([getParamA, getParamB]) => {
|
||||||
const paramA = getParamA(typeA);
|
const paramA = getParamA(typeA);
|
||||||
const paramB = getParamB(typeB);
|
const paramB = getParamB(typeB);
|
||||||
// console.log('request...');
|
// console.log('request...');
|
||||||
return unify(paramA, paramB);
|
return unify(paramA, paramB);
|
||||||
});
|
});
|
||||||
|
return mergeSubstitutionsN(subs);
|
||||||
// merge substitutions
|
|
||||||
unifiedParams.forEach(subst => {
|
|
||||||
for (const [typevar, type] of subst) {
|
|
||||||
attemptReduce(substitutions, typevar, type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
if (e instanceof SubstitutionCycle) {
|
if (e instanceof SubstitutionCycle) {
|
||||||
|
|
@ -164,5 +179,4 @@ export const unify = (typeA, typeB) => {
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
return substitutions;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
import { assignFn } from "../generics/generics.js";
|
// import { assignFn } from "../generics/generics.js";
|
||||||
|
|
||||||
function inspectDynamic(_depth, options, inspect) {
|
function inspectDynamic(_depth, options, inspect) {
|
||||||
return `${inspect(this.i, options)} :: ${inspect(this.t, options)}`;
|
return `${inspect(this.i, options)} :: ${inspect(this.t, options)}`;
|
||||||
|
|
@ -13,14 +13,14 @@ export const newDynamic = i => t => ({
|
||||||
export const getInst = lnk => lnk.i;
|
export const getInst = lnk => lnk.i;
|
||||||
export const getType = lnk => lnk.t;
|
export const getType = lnk => lnk.t;
|
||||||
|
|
||||||
export const apply = input => fun => {
|
// export const apply = input => fun => {
|
||||||
const inputType = getType(input)
|
// const inputType = getType(input)
|
||||||
const funType = getType(fun);
|
// const funType = getType(fun);
|
||||||
const outputType = assignFn(funType, inputType);
|
// const outputType = assignFn(funType, inputType);
|
||||||
|
|
||||||
const inputValue = getInst(input);
|
// const inputValue = getInst(input);
|
||||||
const funValue = getInst(fun);
|
// const funValue = getInst(fun);
|
||||||
const outputValue = funValue(inputValue);
|
// const outputValue = funValue(inputValue);
|
||||||
|
|
||||||
return newDynamic(outputValue)(outputType);
|
// return newDynamic(outputValue)(outputType);
|
||||||
};
|
// };
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
import { getDefaultTypeParser } from "../parser/type_parser.js";
|
||||||
import { apply, getInst, getType, newDynamic } from "./dynamic.js";
|
import { getInst, getType, newDynamic } from "./dynamic.js";
|
||||||
|
|
||||||
const mkType = getDefaultTypeParser();
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
|
|
@ -9,5 +9,4 @@ export const ModuleDynamic = [
|
||||||
["newDynamic", newDynamic(newDynamic)(mkType("a -> Type -> Dynamic" ))],
|
["newDynamic", newDynamic(newDynamic)(mkType("a -> Type -> Dynamic" ))],
|
||||||
["getInst" , newDynamic(getInst )(mkType("Dynamic -> a" ))],
|
["getInst" , newDynamic(getInst )(mkType("Dynamic -> a" ))],
|
||||||
["getType" , newDynamic(getType )(mkType("Dynamic -> Type" ))],
|
["getType" , newDynamic(getType )(mkType("Dynamic -> Type" ))],
|
||||||
["apply" , newDynamic(apply )(mkType("Dynamic -> Dynamic -> Dynamic"))],
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue