better error handling
This commit is contained in:
parent
7edf44f107
commit
428e8cd298
8 changed files with 66 additions and 34 deletions
|
|
@ -60,7 +60,7 @@ export function evalLet(s: LetInBlockState, env: DynamicEnvironment): EvalResult
|
|||
const valueEnv = {
|
||||
names: trie.insert(env.names)(s.name)({
|
||||
recursive: true,
|
||||
i: () => value,
|
||||
i: () => { try { return value; } catch (e) {} },
|
||||
}),
|
||||
};
|
||||
const value = evalExpr(s.value, valueEnv);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Double, eqType, fnType, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, recomputeTypeVars, substitute, SubstitutionCycle, trie, TYPE_VARS, UNBOUND_SYMBOLS, unify } from "dope2";
|
||||
import { Double, eqType, fnType, getHumanReadableName, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, prettySS, prettyT, recomputeTypeVars, substitute, SubstitutionCycle, trie, TYPE_VARS, UNBOUND_SYMBOLS, unify } from "dope2";
|
||||
|
||||
import type { CallBlockState } from "../component/expr/CallBlock";
|
||||
import type { ExprBlockState } from "../component/expr/ExprBlock";
|
||||
|
|
@ -8,8 +8,8 @@ import type { LetInBlockState } from "../component/expr/LetInBlock";
|
|||
import { memoize } from "../util/memoize";
|
||||
|
||||
export interface StaticEnvironment {
|
||||
names: any;
|
||||
typevars: Set<string>;
|
||||
names: any; // mapping from name to type
|
||||
typevars: Set<string>; // set of type variables currently in use
|
||||
}
|
||||
|
||||
export interface Type {
|
||||
|
|
@ -98,7 +98,7 @@ export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState
|
|||
type,
|
||||
subs: new Map(),
|
||||
newEnv,
|
||||
err: new Error("Gibberish"),
|
||||
err: new Error(`'${s.text}' not found`),
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -163,10 +163,15 @@ export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, e
|
|||
});
|
||||
|
||||
export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, env: StaticEnvironment): TypeInfoLet {
|
||||
const recursiveTypeInfo = iterateRecursiveType(s.name, s.value, env);
|
||||
console.log('inferTypeLet..', env.typevars);
|
||||
const recursiveTypeInfo = iterateRecursiveType(s.name, s.value, env, true);
|
||||
console.log('end inferTypeLet.');
|
||||
console.log({recursiveTypeInfo});
|
||||
// to eval the 'inner' expr, we only need to add our parameter to the environment:
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(s.name)({kind: "value", t: recursiveTypeInfo.paramType}),
|
||||
names: trie.insert(env.names)(s.name)({
|
||||
t: recursiveTypeInfo.inner.type,
|
||||
}),
|
||||
typevars: env.typevars,
|
||||
};
|
||||
const innerTypeInfo = inferType(s.inner, innerEnv);
|
||||
|
|
@ -182,7 +187,9 @@ export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, en
|
|||
});
|
||||
|
||||
export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockState, env: StaticEnvironment): TypeInfoLambda {
|
||||
const recursiveTypeInfo = iterateRecursiveType(s.paramName, s.expr, env);
|
||||
console.log('inferTypeLambda..', env.typevars);
|
||||
const recursiveTypeInfo = iterateRecursiveType(s.paramName, s.expr, env, false);
|
||||
console.log('end inferTypeLambda.');
|
||||
return {
|
||||
kind: "lambda",
|
||||
type: fnType(_ => recursiveTypeInfo.paramType)(_ => recursiveTypeInfo.inner.type),
|
||||
|
|
@ -192,21 +199,36 @@ export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockSt
|
|||
|
||||
// Given a named value whose type we know nothing about, and an expression that computes the value (which may recursively contain the value), compute the type of the value.
|
||||
// Why? Both lambda functions and let-expressions can refer to themselves recursively. To infer their type, we need to recompute the type and feed it back to itself until some fixed point is reached.
|
||||
function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: StaticEnvironment) {
|
||||
function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: StaticEnvironment, paramIsInner: boolean) {
|
||||
let [paramType] = typeUnknown(env);
|
||||
const paramTypeVar = paramType.symbol;
|
||||
|
||||
let iterations = 1;
|
||||
let iterations = 0;
|
||||
while (true) {
|
||||
const innerEnv = {
|
||||
names: trie.insert(env.names)(paramName)({kind: "unknown", t: paramType}),
|
||||
names: trie.insert(env.names)(paramName)({
|
||||
kind: "unknown",
|
||||
t: paramType,
|
||||
}),
|
||||
typevars: env.typevars.union(occurring(paramType) as Set<string>),
|
||||
};
|
||||
|
||||
const innerTypeInfo = inferType(expr, innerEnv);
|
||||
const subsWithoutPType = new Map(innerTypeInfo.subs);
|
||||
subsWithoutPType.delete(paramTypeVar);
|
||||
|
||||
const inferredPType = substitute(paramType, innerTypeInfo.subs, []);
|
||||
const [inferredPType2, newEnv] = rewriteInferredType(inferredPType, env);
|
||||
|
||||
const subsWithoutPType = new Map(innerTypeInfo.subs); // copy
|
||||
subsWithoutPType.delete(paramTypeVar);
|
||||
|
||||
// console.log("-----------------", iterations);
|
||||
// console.log("paramType:", prettyT(paramType));
|
||||
// console.log("inferredPType:", prettyT(inferredPType));
|
||||
// console.log("inferredPType2:", prettyT(inferredPType2));
|
||||
// console.log("env:", [...env.typevars].map(getHumanReadableName));
|
||||
// console.log("innerEnv:", [...innerEnv.typevars].map(getHumanReadableName));
|
||||
// console.log("-----------------");
|
||||
|
||||
if (eqType(inferredPType2)(paramType)) {
|
||||
return {
|
||||
subs: subsWithoutPType,
|
||||
|
|
@ -216,17 +238,11 @@ function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: Stat
|
|||
innerEnv,
|
||||
};
|
||||
}
|
||||
if ((iterations++) == 100) {
|
||||
|
||||
if ((iterations++) == 10) {
|
||||
throw new Error("too many iterations! something's wrong!");
|
||||
}
|
||||
// console.log("-----------------", iterations);
|
||||
// console.log("paramType:", prettyT(paramType));
|
||||
// console.log("inferredPType:", prettyT(inferredPType));
|
||||
// console.log("inferredPType2:", prettyT(inferredPType2));
|
||||
// console.log("env:", [...env.typevars].map(getHumanReadableName));
|
||||
// console.log("innerEnv:", [...innerEnv.typevars].map(getHumanReadableName));
|
||||
// console.log("-----------------");
|
||||
paramType = inferredPType2;
|
||||
paramType = inferredPType2; // next iteration
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +275,7 @@ function rewriteInferredType(type: Type, env: StaticEnvironment): [Type, StaticE
|
|||
const newTypeVars = new Set(env.typevars);
|
||||
let i = 0;
|
||||
for (const o of occurring(type)) {
|
||||
while (env.typevars.has(UNBOUND_SYMBOLS[i])) {
|
||||
while (newTypeVars.has(UNBOUND_SYMBOLS[i])) {
|
||||
i++;
|
||||
}
|
||||
if (!env.typevars.has(o)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue