wrote interpreter (independent of type inferencer)
This commit is contained in:
parent
2279c54229
commit
32bdc23ea7
6 changed files with 104 additions and 602 deletions
|
|
@ -7,6 +7,7 @@ import { actionShortcuts } from './actions';
|
||||||
import { biggerExample, emptySet, factorial, higherOrder, higherOrder2Params, inc, initialEditorState, lambda2Params, nonEmptyEditorState, pushBool, setOfListOfBool, tripleFunctionCallEditorState } from "./configurations";
|
import { biggerExample, emptySet, factorial, higherOrder, higherOrder2Params, inc, initialEditorState, lambda2Params, nonEmptyEditorState, pushBool, setOfListOfBool, tripleFunctionCallEditorState } from "./configurations";
|
||||||
|
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
import { evalExpr } from '../../eval/eval';
|
||||||
|
|
||||||
|
|
||||||
const examples: [string, ExprBlockState][] = [
|
const examples: [string, ExprBlockState][] = [
|
||||||
|
|
@ -181,6 +182,7 @@ export function App() {
|
||||||
}}
|
}}
|
||||||
typeInfo={typeInfo}
|
typeInfo={typeInfo}
|
||||||
/>
|
/>
|
||||||
|
={evalExpr(currentState, extendedEnv)}
|
||||||
</GlobalContext>
|
</GlobalContext>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useContext } from "react";
|
||||||
import { CallContext } from "../../context/CallContext";
|
import { CallContext } from "../../context/CallContext";
|
||||||
import { EnvContext } from "../../context/EnvContext";
|
import { EnvContext } from "../../context/EnvContext";
|
||||||
import { GlobalContext } from "../../context/GlobalContext";
|
import { GlobalContext } from "../../context/GlobalContext";
|
||||||
import { type Environment, type TypeInfoCall } from "../../eval/infer_type";
|
import { type StaticEnvironment, type TypeInfoCall } from "../../eval/infer_type";
|
||||||
import { getActions } from "../app/actions";
|
import { getActions } from "../app/actions";
|
||||||
import { Type } from "../other/Type";
|
import { Type } from "../other/Type";
|
||||||
import "./CallBlock.css";
|
import "./CallBlock.css";
|
||||||
|
|
@ -22,7 +22,7 @@ export interface CallBlockProps<
|
||||||
typeInfo: TypeInfoCall;
|
typeInfo: TypeInfoCall;
|
||||||
}
|
}
|
||||||
|
|
||||||
function nestedFnProperties({state, setState, score, typeInfo}: CallBlockProps, env: Environment) {
|
function nestedFnProperties({state, setState, score, typeInfo}: CallBlockProps, env: StaticEnvironment) {
|
||||||
const setFn = (callback: SetStateFn) => {
|
const setFn = (callback: SetStateFn) => {
|
||||||
setState(state => ({...state, fn: callback(state.fn)}));
|
setState(state => ({...state, fn: callback(state.fn)}));
|
||||||
};
|
};
|
||||||
|
|
@ -35,7 +35,7 @@ function nestedFnProperties({state, setState, score, typeInfo}: CallBlockProps,
|
||||||
return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn, typeInfo: typeInfo.fn};
|
return {state: state.fn, setState: setFn, onCancel: onFnCancel, score: scoreFn, typeInfo: typeInfo.fn};
|
||||||
}
|
}
|
||||||
|
|
||||||
function nestedInputProperties({state, setState, score, typeInfo}: CallBlockProps, env: Environment) {
|
function nestedInputProperties({state, setState, score, typeInfo}: CallBlockProps, env: StaticEnvironment) {
|
||||||
const setInput = (callback: SetStateFn) => {
|
const setInput = (callback: SetStateFn) => {
|
||||||
setState(state => ({...state, input: callback(state.input)}));
|
setState(state => ({...state, input: callback(state.input)}));
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { trie } from "dope2";
|
||||||
import { CallContext } from "../../context/CallContext";
|
import { CallContext } from "../../context/CallContext";
|
||||||
import { EnvContext } from "../../context/EnvContext";
|
import { EnvContext } from "../../context/EnvContext";
|
||||||
import { GlobalContext } from "../../context/GlobalContext";
|
import { GlobalContext } from "../../context/GlobalContext";
|
||||||
import { inferTypeInput, type Environment, type Type, type TypeInfoInput } from "../../eval/infer_type";
|
import { inferTypeInput, type StaticEnvironment, type Type, type TypeInfoInput } from "../../eval/infer_type";
|
||||||
import { getActions } from "../app/actions";
|
import { getActions } from "../app/actions";
|
||||||
import { Input } from "../other/Input";
|
import { Input } from "../other/Input";
|
||||||
import { Type as TypeBlock } from "../other/Type";
|
import { Type as TypeBlock } from "../other/Type";
|
||||||
|
|
@ -46,9 +46,11 @@ const attemptLiterals = [
|
||||||
|
|
||||||
const computeSuggestions = (
|
const computeSuggestions = (
|
||||||
text: string,
|
text: string,
|
||||||
env: Environment,
|
env: StaticEnvironment,
|
||||||
score: InputBlockProps['score'],
|
score: InputBlockProps['score'],
|
||||||
): PrioritizedSuggestionType[] => {
|
): PrioritizedSuggestionType[] => {
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
const ls = [
|
const ls = [
|
||||||
...attemptLiterals
|
...attemptLiterals
|
||||||
.filter(([_, test]) => test(text))
|
.filter(([_, test]) => test(text))
|
||||||
|
|
@ -69,9 +71,17 @@ const computeSuggestions = (
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
const midTime = performance.now();
|
||||||
|
|
||||||
// return []; // <-- uncomment to disable suggestions (useful for debugging)
|
// return []; // <-- uncomment to disable suggestions (useful for debugging)
|
||||||
return ls.map((state) => [score(state), inferTypeInput(state, env).type, state] as PrioritizedSuggestionType)
|
const result = ls.map((state) => [score(state), inferTypeInput(state, env).type, state] as PrioritizedSuggestionType)
|
||||||
.sort(([a],[b]) => b-a);
|
.sort(([a],[b]) => b-a);
|
||||||
|
|
||||||
|
const endTime = performance.now();
|
||||||
|
|
||||||
|
console.log(`computing suggestions took ${midTime-startTime} + ${endTime - midTime}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function InputBlock({ state, setState, score, onCancel, typeInfo }: InputBlockProps) {
|
export function InputBlock({ state, setState, score, onCancel, typeInfo }: InputBlockProps) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { createContext } from "react";
|
import { createContext } from "react";
|
||||||
import { getDefaultTypeParser, module2Env, ModuleStd } from "dope2";
|
import { getDefaultTypeParser, module2Env, ModuleStd } from "dope2";
|
||||||
// import type { Dynamic, Environment } from "./eval";
|
// import type { Dynamic, Environment } from "./eval";
|
||||||
import type { Environment } from "../eval/infer_type";
|
import type { StaticEnvironment } from "../eval/infer_type";
|
||||||
|
|
||||||
export const functionWith3Params = i => j => k => i+j+k;
|
export const functionWith3Params = i => j => k => i+j+k;
|
||||||
export const functionWith4Params = i => j => k => l => i+j+k+l;
|
export const functionWith4Params = i => j => k => l => i+j+k+l;
|
||||||
|
|
||||||
const mkType = getDefaultTypeParser();
|
const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
export const extendedEnv: Environment = {
|
export const extendedEnv: StaticEnvironment = {
|
||||||
names: module2Env(ModuleStd.concat([
|
names: module2Env(ModuleStd.concat([
|
||||||
["true", {i: true, t: mkType("Bool")}],
|
["true", {i: true, t: mkType("Bool")}],
|
||||||
["false", {i: false, t: mkType("Bool")}],
|
["false", {i: false, t: mkType("Bool")}],
|
||||||
|
|
|
||||||
652
src/eval/eval.ts
652
src/eval/eval.ts
|
|
@ -1,588 +1,78 @@
|
||||||
// import { Double, eqType, fnType, getHumanReadableName, getSymbol, Int, isTypeVar, occurring, prettyT, recomputeTypeVarsWithInverse, substitute, symbolFunction, transitivelyGrow, trie, TYPE_VARS, UNBOUND_SYMBOLS } from "dope2";
|
import { trie } from "dope2";
|
||||||
|
import type { CallBlockState } from "../component/expr/CallBlock";
|
||||||
|
import type { ExprBlockState } from "../component/expr/ExprBlock";
|
||||||
|
import type { InputBlockState } from "../component/expr/InputBlock";
|
||||||
|
import type { LambdaBlockState } from "../component/expr/LambdaBlock";
|
||||||
|
import type { LetInBlockState } from "../component/expr/LetInBlock";
|
||||||
|
|
||||||
// import type { ExprBlockState } from "./ExprBlock";
|
export interface DynamicEnvironment {
|
||||||
// import type { InputValueType } from "./InputBlock";
|
names: any;
|
||||||
|
}
|
||||||
|
|
||||||
// const IS_DEV = (import.meta.env.MODE === "development");
|
export type EvalResult = any;
|
||||||
// const VERBOSE = true && IS_DEV;
|
|
||||||
|
|
||||||
// export interface Environment {
|
export function evalExpr(s: ExprBlockState, env: DynamicEnvironment): EvalResult {
|
||||||
// names: any;
|
if (s.kind === "input") {
|
||||||
// nextFreeTypeVar: number;
|
return evalInput(s, env);
|
||||||
// typeVars: Set<string>;
|
}
|
||||||
// }
|
else if (s.kind === "call") {
|
||||||
|
return evalCall(s, env);
|
||||||
|
}
|
||||||
|
else if (s.kind === "let") {
|
||||||
|
return evalLet(s, env);
|
||||||
|
}
|
||||||
|
else { // (s.kind === "lambda")
|
||||||
|
return evalLambda(s, env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// interface Type {
|
export function evalInput(s: InputBlockState, env: DynamicEnvironment): EvalResult {
|
||||||
// symbol: string;
|
if (s.value.kind === "literal") {
|
||||||
// params: any[];
|
const ctor = {
|
||||||
// };
|
Int: BigInt,
|
||||||
|
Double: Number,
|
||||||
|
}[s.value.type] as (s: string) => any;
|
||||||
|
return ctor(s.text);
|
||||||
|
}
|
||||||
|
else if (s.value.kind === "name") {
|
||||||
|
const found = trie.get(env.names)(s.text);
|
||||||
|
if (found) {
|
||||||
|
if (found.recursive) {
|
||||||
|
return found.i();
|
||||||
|
}
|
||||||
|
return found.i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// type Unification = Map<string, any>;
|
export function evalCall(s: CallBlockState, env: DynamicEnvironment): EvalResult {
|
||||||
|
const fn = evalExpr(s.fn, env);
|
||||||
|
const input = evalExpr(s.input, env);
|
||||||
|
if (fn !== undefined && input !== undefined) {
|
||||||
|
return fn(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// export interface DeepError {
|
export function evalLet(s: LetInBlockState, env: DynamicEnvironment): EvalResult {
|
||||||
// kind: "error";
|
const valueEnv = {
|
||||||
// e: Error;
|
names: trie.insert(env.names)(s.name)({
|
||||||
// depth: number;
|
recursive: true,
|
||||||
// t: Type;
|
i: () => value,
|
||||||
// unification: Unification;
|
}),
|
||||||
// }
|
};
|
||||||
|
const value = evalExpr(s.value, valueEnv);
|
||||||
|
const innerEnv = {
|
||||||
|
names: trie.insert(env.names)(s.name)({i: value}),
|
||||||
|
}
|
||||||
|
return evalExpr(s.inner, innerEnv);
|
||||||
|
}
|
||||||
|
|
||||||
// // a dynamically typed value = tuple (instance, type)
|
export function evalLambda(s: LambdaBlockState, env: DynamicEnvironment): EvalResult {
|
||||||
// export interface Dynamic {
|
const fn = x => {
|
||||||
// kind: "value",
|
const innerEnv = {
|
||||||
// i: any;
|
names: trie.insert(env.names)(s.paramName)({i: x})
|
||||||
// t: Type;
|
};
|
||||||
// unification: Unification;
|
return evalExpr(s.expr, innerEnv);
|
||||||
// };
|
};
|
||||||
|
return fn;
|
||||||
// export interface Unknown {
|
}
|
||||||
// kind: "unknown";
|
|
||||||
// t: Type;
|
|
||||||
// unification: Unification;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // the value of every block is either known (Dynamic), an error, or unknown
|
|
||||||
// export type ResolvedType = Dynamic | DeepError | Unknown;
|
|
||||||
|
|
||||||
// class NotFoundError extends Error {}
|
|
||||||
|
|
||||||
// export const evalExprBlock = (s: ExprBlockState, env: Environment): [ResolvedType,Environment] => {
|
|
||||||
// if (s.kind === "input") {
|
|
||||||
// return evalInputBlock(s.text, s.value, env);
|
|
||||||
// }
|
|
||||||
// else if (s.kind === "call") {
|
|
||||||
// return evalCallBlock(s.fn, s.input, env);
|
|
||||||
// }
|
|
||||||
// else if (s.kind === "let") {
|
|
||||||
// return evalLetInBlock(s.value, s.name, s.inner, env);
|
|
||||||
// }
|
|
||||||
// else { // (s.kind === "lambda")
|
|
||||||
// const [resolved, env2] = evalLambdaBlock(s.paramName, s.expr, env);
|
|
||||||
// return [resolved, env2];
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export function evalInputBlock(text: string, value: InputValueType, env: Environment): [ResolvedType,Environment] {
|
|
||||||
// if (value.kind === "literal") {
|
|
||||||
// return parseLiteral(text, value.type, env);
|
|
||||||
// }
|
|
||||||
// else if (value.kind === "name") {
|
|
||||||
// const found = trie.get(env.names)(text);
|
|
||||||
// if (found) {
|
|
||||||
// return [found, env]; // don't rewrite lambda parameters
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// const [t, env2] = makeTypeVar(env, 'err')
|
|
||||||
// return [{
|
|
||||||
// kind: "error",
|
|
||||||
// t,
|
|
||||||
// e: new NotFoundError(`'${text}' not found`),
|
|
||||||
// depth: 0,
|
|
||||||
// unification: new Map(),
|
|
||||||
// }, env2];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function evalCallBlock(fn: ExprBlockState, input: ExprBlockState, env: Environment): [ResolvedType,Environment] {
|
|
||||||
// let __debug = `
|
|
||||||
// ========= evalCallBlock =========
|
|
||||||
// env.typeVars : ${[...env.typeVars].map(getHumanReadableName)}
|
|
||||||
// env.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env.nextFreeTypeVar])}
|
|
||||||
// fn.kind : ${fn.kind}
|
|
||||||
// input.kind : ${input.kind}`;
|
|
||||||
// let [fnResolved, env2] = evalExprBlock(fn, env);
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// ==== evalCallBlock (fn) ====
|
|
||||||
// fn.t : ${prettyT(fnResolved.t)}
|
|
||||||
// env2.typeVars : ${[...env2.typeVars].map(getHumanReadableName)}
|
|
||||||
// env2.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env2.nextFreeTypeVar])}`;
|
|
||||||
// }
|
|
||||||
// if (fnResolved.kind !== "unknown") {
|
|
||||||
// let fnInverse;
|
|
||||||
// [fnResolved, env2, fnInverse] = recomputeTypeVarsForEnv("<name unused>", fnResolved, env2);
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// --- recomp ---
|
|
||||||
// fn.t : ${prettyT(fnResolved.t)}
|
|
||||||
// fnInverse : ${[...fnInverse].map(([symbol,type])=>`${getHumanReadableName(symbol)} => ${getHumanReadableName(type)})`).join(',')}
|
|
||||||
// env2.typeVars : ${[...env2.typeVars].map(getHumanReadableName)}
|
|
||||||
// env2.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env2.nextFreeTypeVar])}`;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// let [inputResolved, env3] = evalExprBlock(input, env2);
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// ==== evalCallBlock (input) ====
|
|
||||||
// input.t : ${prettyT(inputResolved.t)}
|
|
||||||
// env3.typeVars : ${[...env3.typeVars].map(getHumanReadableName)}
|
|
||||||
// env3.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env3.nextFreeTypeVar])}`;
|
|
||||||
// }
|
|
||||||
// if (inputResolved.kind !== "unknown") {
|
|
||||||
// let inputInverse;
|
|
||||||
// [inputResolved, env3, inputInverse] = recomputeTypeVarsForEnv("<name unused>", inputResolved, env3);
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// --- recomp ---
|
|
||||||
// input.t : ${prettyT(inputResolved.t)}
|
|
||||||
// inputInverse : ${[...inputInverse].map(([symbol,type])=>`${getHumanReadableName(symbol)} => ${getHumanReadableName(type)})`).join(',')}
|
|
||||||
// env3.typeVars : ${[...env3.typeVars].map(getHumanReadableName)}
|
|
||||||
// env3.nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env3.nextFreeTypeVar])}`;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// const result = evalCallBlock2(fnResolved, inputResolved, env3);
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// // @ts-ignore
|
|
||||||
// result[0].__debug = __debug + (result[0].__debug || '');
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function evalCallBlock2(fnResolved: ResolvedType, inputResolved: ResolvedType, env: Environment): [ResolvedType,Environment] {
|
|
||||||
// if (getSymbol(fnResolved.t) !== symbolFunction) {
|
|
||||||
// // not a function...
|
|
||||||
// if (isTypeVar(fnResolved.t)) {
|
|
||||||
// // ... but typeVars are OK (good guys!)
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// // worst outcome
|
|
||||||
// return makeError(env,
|
|
||||||
// new NotAFunctionError(`${prettyT(fnResolved.t)} is not a function type!`),
|
|
||||||
// mergeUnifications(fnResolved.unification, inputResolved.unification),
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // it's is a function, continue...
|
|
||||||
// return evalCallBlock3(fnResolved, inputResolved, env);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const highestTypeVar = type => {
|
|
||||||
// let highest = -1;
|
|
||||||
// for (const typeVar of occurring(type)) {
|
|
||||||
// highest = Math.max(highest, UNBOUND_SYMBOLS.indexOf(typeVar));
|
|
||||||
// }
|
|
||||||
// return highest;
|
|
||||||
// }
|
|
||||||
// const highestTypeVar2 = typeVars => {
|
|
||||||
// let highest = -1;
|
|
||||||
// for (const typeVar of typeVars) {
|
|
||||||
// highest = Math.max(highest, UNBOUND_SYMBOLS.indexOf(typeVar));
|
|
||||||
// }
|
|
||||||
// return highest;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// const inverseUnification = (uni, inverse) => {
|
|
||||||
// return new Map([...uni]
|
|
||||||
// .filter(([symbol]) => !inverse.has(inverse.get(symbol)))
|
|
||||||
// .map(([symbol, types]) => [inverse.get(symbol) || symbol, types])
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function recomputeTypeVarsForEnv(name: string, resolved: ResolvedType, env: Environment): [ResolvedType,Environment,any] {
|
|
||||||
// const [[newType], inverse] = recomputeTypeVarsWithInverse([resolved.t], findTempIdx(env));
|
|
||||||
// const newResolved: ResolvedType = {
|
|
||||||
// ...resolved,
|
|
||||||
// t: newType,
|
|
||||||
// unification: inverseUnification(resolved.unification, inverse),
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // hacky
|
|
||||||
// const typeVars = env.typeVars.union(occurring(newType)) as Set<string>;
|
|
||||||
// const newEnv: Environment = {
|
|
||||||
// // names: trie.insert(env.names)(name)(newResolved),
|
|
||||||
// names: env.names,
|
|
||||||
// typeVars,
|
|
||||||
// nextFreeTypeVar: highestTypeVar2(typeVars) + 1,
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return [newResolved, newEnv, inverse];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function removeTypeVarsFromEnv(toRemove, env: Environment): Environment {
|
|
||||||
// const newTypeVars = env.typeVars.difference(toRemove);
|
|
||||||
// return {
|
|
||||||
// names: env.names,
|
|
||||||
// typeVars: newTypeVars,
|
|
||||||
// nextFreeTypeVar: highestTypeVar2(newTypeVars) + 1,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function evalCallBlock3(fnResolved: ResolvedType, inputResolved: ResolvedType, env: Environment): [ResolvedType,Environment] {
|
|
||||||
// let __debug = '';
|
|
||||||
// try {
|
|
||||||
// if (occurring(fnResolved.t).intersection(occurring(inputResolved.t)).size > 0) {
|
|
||||||
// throw new Error(`Precondition failed: function (${prettyT(fnResolved.t)}) and its input (${prettyT(inputResolved.t)}) have overlapping typevars!`);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const typeVarsOfFnAndInput = occurring(fnResolved.t).union(occurring(inputResolved.t));
|
|
||||||
|
|
||||||
// // turn input in to a function
|
|
||||||
// const [abstractOutputType, env2] = makeTypeVar(env, "<out>");
|
|
||||||
// const matchFnType = fnType(_ => inputResolved.t)(_ => abstractOutputType);
|
|
||||||
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// ========= evalCallBlock3 =========
|
|
||||||
// env.typeVars : ${[...env.typeVars].map(getHumanReadableName)}
|
|
||||||
// nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env.nextFreeTypeVar])}
|
|
||||||
// fnKind : ${fnResolved.kind}
|
|
||||||
// inputKind : ${inputResolved.kind}
|
|
||||||
// fnType : ${prettyT(fnResolved.t)}
|
|
||||||
// inputType : ${prettyT(inputResolved.t)}
|
|
||||||
// matchFnType : ${prettyT(matchFnType)}`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // unify both functions
|
|
||||||
// const unification = /*transitivelyGrow*/(unifyLL(fnResolved.t, matchFnType));
|
|
||||||
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// unification : ${prettyU(unification)}`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const unificationR = reduceUnification(unification);
|
|
||||||
// const unifiedFnType = substitute(
|
|
||||||
// // matchFnType,
|
|
||||||
// fnResolved.t,
|
|
||||||
// unificationR, []);
|
|
||||||
|
|
||||||
// const outType = unifiedFnType.params[1](unifiedFnType);
|
|
||||||
|
|
||||||
// const typeVarsOfOutType = occurring(outType);
|
|
||||||
|
|
||||||
// const removedTypeVars = typeVarsOfFnAndInput.difference(typeVarsOfOutType);
|
|
||||||
|
|
||||||
// // const newEnv = (outType === abstractOutputType) ? env2 : env;
|
|
||||||
// const newEnv = removeTypeVarsFromEnv(removedTypeVars, env);
|
|
||||||
|
|
||||||
// // we don't want to 'bubble up' our outType substitution, because it's just a temporary variable
|
|
||||||
// const unificationWithoutOutType = new Map([...unification].filter(([symbol]) => symbol !== abstractOutputType.symbol));
|
|
||||||
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// unificationR : ${prettyRU(unificationR)}
|
|
||||||
// unifiedFnType : ${prettyT(unifiedFnType)}
|
|
||||||
// outType : ${prettyT(outType)}
|
|
||||||
// removedTypeVars : ${[...removedTypeVars].map(getHumanReadableName)}
|
|
||||||
// fn.unification : ${prettyU(fnResolved.unification)}
|
|
||||||
// input.unification : ${prettyU(inputResolved.unification)}
|
|
||||||
// unificationWithoutOutType: ${prettyU(unificationWithoutOutType)}`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const grandUnification = transitivelyGrow([fnResolved.unification, inputResolved.unification]
|
|
||||||
// .reduce(mergeUnifications, unificationWithoutOutType));
|
|
||||||
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// grandUnification : ${prettyU(grandUnification)}
|
|
||||||
// newEnv.typeVars : ${[...newEnv.typeVars].map(getHumanReadableName)}
|
|
||||||
// newEnv.newFreeTypeVar : ${getHumanReadableName(UNBOUND_SYMBOLS[newEnv.nextFreeTypeVar])}`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// if (inputResolved.kind === "error") {
|
|
||||||
// // throw inputResolved.e;
|
|
||||||
// return [{
|
|
||||||
// kind: "error",
|
|
||||||
// e: inputResolved.e, // bubble up the error
|
|
||||||
// depth: 0,
|
|
||||||
// t: outType,
|
|
||||||
// unification: grandUnification,
|
|
||||||
// // @ts-ignore
|
|
||||||
// __debug,
|
|
||||||
// }, newEnv];
|
|
||||||
// }
|
|
||||||
// if (fnResolved.kind === "error") {
|
|
||||||
// // throw fnResolved.e;
|
|
||||||
// // also bubble up
|
|
||||||
// return [{
|
|
||||||
// kind: "error",
|
|
||||||
// e: fnResolved.e,
|
|
||||||
// depth: fnResolved.depth+1,
|
|
||||||
// t: outType,
|
|
||||||
// unification: grandUnification,
|
|
||||||
// // @ts-ignore
|
|
||||||
// __debug,
|
|
||||||
// }, newEnv];
|
|
||||||
// }
|
|
||||||
// // if the above statement did not throw => types are compatible...
|
|
||||||
// if (inputResolved.kind === "value" && fnResolved.kind === "value") {
|
|
||||||
// const outValue = fnResolved.i(inputResolved.i);
|
|
||||||
// // console.log('outValue:', outValue);
|
|
||||||
// return [{
|
|
||||||
// kind: "value",
|
|
||||||
// i: outValue,
|
|
||||||
// t: outType,
|
|
||||||
// unification: grandUnification,
|
|
||||||
// // @ts-ignore
|
|
||||||
// __debug,
|
|
||||||
// }, newEnv];
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// // we don't know the value, but we do know the type:
|
|
||||||
// return [{
|
|
||||||
// kind: "unknown",
|
|
||||||
// t: outType,
|
|
||||||
// unification: grandUnification,
|
|
||||||
// // @ts-ignore
|
|
||||||
// __debug,
|
|
||||||
// }, newEnv];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (e) {
|
|
||||||
// // if ((e instanceof UnifyError)) {
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += '\n\nUnifyError! ' + (e as Error).message;
|
|
||||||
// }
|
|
||||||
// const err = makeError(env, e as Error);
|
|
||||||
// // @ts-ignore
|
|
||||||
// err[0].__debug = __debug;
|
|
||||||
// return err;
|
|
||||||
// // }
|
|
||||||
// throw e;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function evalLetInBlock(value: ExprBlockState, name: string, inner: ExprBlockState, env: Environment): [ResolvedType,Environment] {
|
|
||||||
// const [valueResolved] = evalExprBlock(value, env);
|
|
||||||
// const innerEnv = makeInnerEnv(env, name, valueResolved);
|
|
||||||
// return evalExprBlock(inner, innerEnv);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const prettyRU = (rUni: Map<string, Type>) => {
|
|
||||||
// return '{'+[...rUni].map(([symbol,type]) => `${getHumanReadableName(symbol)} => ${prettyT(type)}`).join(', ')+'}';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function fixPoint(varName: string, expr: ExprBlockState, env: Environment) {
|
|
||||||
// let [varType, innerEnv] = makeTypeVar(env, varName);
|
|
||||||
// let round = 0;
|
|
||||||
// let __debug = '';
|
|
||||||
// while (true) {
|
|
||||||
// const [innerResolved] = evalExprBlock(expr, innerEnv);
|
|
||||||
// const newInnerT = substitute(innerResolved.t, reduceUnification(innerResolved.unification), []);
|
|
||||||
|
|
||||||
// round++;
|
|
||||||
// if (round === 10) {
|
|
||||||
// throw new Error("too many rounds!");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function evalLambdaBlock(paramName: string, expr: ExprBlockState, env: Environment): [ResolvedType,Environment,Environment] {
|
|
||||||
// let [paramType, innerEnv] = makeTypeVar(env, paramName);
|
|
||||||
// let round = 0;
|
|
||||||
// let __debug = '';
|
|
||||||
// while (true) {
|
|
||||||
// // fixpoint computation...
|
|
||||||
// const [exprResolved] = evalExprBlock(expr, innerEnv);
|
|
||||||
// const lambdaT = fnType(_ => paramType)(_ => exprResolved.t);
|
|
||||||
// // This is the only place in the code where we actually do something with the 'substitutions'. We compute the type of our lambda function:
|
|
||||||
// const reduced = reduceUnification(exprResolved.unification);
|
|
||||||
// const lambdaTSubstituted = substitute(
|
|
||||||
// lambdaT,
|
|
||||||
// reduced,
|
|
||||||
// []); // <- not important
|
|
||||||
|
|
||||||
// let lambdaResolved: ResolvedType;
|
|
||||||
// if (exprResolved.kind === "error") {
|
|
||||||
// lambdaResolved = {
|
|
||||||
// kind: "error",
|
|
||||||
// t: lambdaTSubstituted,
|
|
||||||
// depth: 0,
|
|
||||||
// e: exprResolved.e,
|
|
||||||
// unification: exprResolved.unification,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// lambdaResolved = {
|
|
||||||
// kind: "value",
|
|
||||||
// t: lambdaTSubstituted,
|
|
||||||
// i: (x: any) => {
|
|
||||||
// const innerEnv = makeInnerEnv(env, paramName, {
|
|
||||||
// kind: "value",
|
|
||||||
// i: x,
|
|
||||||
// t: lambdaTSubstituted,
|
|
||||||
// unification: new Map(),
|
|
||||||
// });
|
|
||||||
// const [result] = evalExprBlock(expr, innerEnv);
|
|
||||||
// if (result.kind === "value") {
|
|
||||||
// return result.i;
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// unification: exprResolved.unification,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // const [betterResolved, newInnerEnv] = recomputeTypeVarsForEnv("<name unused>", lambdaResolved, env);
|
|
||||||
// const [betterResolved, newInnerEnv] = [lambdaResolved, env];
|
|
||||||
|
|
||||||
// const inferredParamType = betterResolved.t.params[0](betterResolved.t);
|
|
||||||
|
|
||||||
// if (VERBOSE) {
|
|
||||||
// __debug += `
|
|
||||||
// ====== evalLambdaBlock (round ${round}) ======
|
|
||||||
// env.typeVars : ${[...env.typeVars].map(getHumanReadableName)}
|
|
||||||
// nextFreeTypeVar: ${getHumanReadableName(UNBOUND_SYMBOLS[env.nextFreeTypeVar])}
|
|
||||||
// paramName : ${paramName}
|
|
||||||
// paramType : ${prettyT(paramType)}
|
|
||||||
// innerEnv.typevars : ${[...innerEnv.typeVars].map(getHumanReadableName)}
|
|
||||||
// paramType : ${prettyT(paramType)}
|
|
||||||
// exprType : ${prettyT(exprResolved.t)}
|
|
||||||
// exprUnification : ${prettyU(exprResolved.unification)}
|
|
||||||
// exprUnificationR : ${prettyRU(reduced)}
|
|
||||||
// lambdaType : ${prettyT(lambdaT)}
|
|
||||||
// lambdaTypeSubsituted: ${prettyT(lambdaTSubstituted)}
|
|
||||||
// betterResolvedT : ${prettyT(betterResolved.t)}
|
|
||||||
// inferredParamType : ${prettyT(inferredParamType)}`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (eqType(paramType)(inferredParamType)) {
|
|
||||||
// // reached fixpoint
|
|
||||||
// // @ts-ignore
|
|
||||||
// lambdaResolved.__debug = __debug;
|
|
||||||
// return [lambdaResolved, env, innerEnv];
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// paramType = inferredParamType;
|
|
||||||
// innerEnv = {
|
|
||||||
// ...newInnerEnv,
|
|
||||||
// names: trie.insert(newInnerEnv.names)(paramName)({
|
|
||||||
// kind: "unknown",
|
|
||||||
// t: inferredParamType,
|
|
||||||
// unification: new Map(), // <-- ??
|
|
||||||
// }),
|
|
||||||
// };
|
|
||||||
// round++;
|
|
||||||
// if (round === 10) {
|
|
||||||
// throw new Error("too many rounds!");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// export function scoreResolved(resolved: ResolvedType, outPriority: (s:ResolvedType) => number) {
|
|
||||||
// const bias = outPriority(resolved);
|
|
||||||
|
|
||||||
// if (resolved.kind === "value") {
|
|
||||||
// return bias;
|
|
||||||
// }
|
|
||||||
// else if (resolved.kind === "unknown") {
|
|
||||||
// return bias;
|
|
||||||
// }
|
|
||||||
// if (resolved.e instanceof UnifyError) {
|
|
||||||
// return -1 + bias; // parameter doesn't match
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return -2 + bias; // even worse: fn is not a function!
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function makeInnerEnv(env: Environment, name: string, value: ResolvedType): Environment {
|
|
||||||
// if (name !== "") {
|
|
||||||
// return {
|
|
||||||
// names: trie.insert(env.names)(name)(value),
|
|
||||||
// nextFreeTypeVar: env.nextFreeTypeVar,
|
|
||||||
// typeVars: env.typeVars,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return env;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function findTempIdx(env: Environment) {
|
|
||||||
// let idx = 16;
|
|
||||||
// while (env.typeVars.has(UNBOUND_SYMBOLS[idx])) {
|
|
||||||
// idx++;
|
|
||||||
// }
|
|
||||||
// return idx;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function makeTypeVar(env: Environment, name: string): [Type, Environment] {
|
|
||||||
// let idx = 0;
|
|
||||||
// while (env.typeVars.has(UNBOUND_SYMBOLS[idx])) {
|
|
||||||
// idx++;
|
|
||||||
// }
|
|
||||||
// const typeVar = TYPE_VARS[idx];
|
|
||||||
// const unknown: Unknown = {
|
|
||||||
// kind: "unknown",
|
|
||||||
// t: typeVar,
|
|
||||||
// unification: new Map(),
|
|
||||||
// };
|
|
||||||
// return [ typeVar, {
|
|
||||||
// names: trie.insert(env.names)(name)(unknown),
|
|
||||||
// nextFreeTypeVar: idx + 1,
|
|
||||||
// typeVars: new Set([...env.typeVars, UNBOUND_SYMBOLS[idx]]),
|
|
||||||
// }];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function makeError(env: Environment, e: Error, unification: Unification=new Map()): [DeepError, Environment] {
|
|
||||||
// let idx = 0;
|
|
||||||
// while (env.typeVars.has(UNBOUND_SYMBOLS[idx])) {
|
|
||||||
// idx++;
|
|
||||||
// }
|
|
||||||
// const typeVar = TYPE_VARS[idx];
|
|
||||||
// const deepError: DeepError = {
|
|
||||||
// kind: "error",
|
|
||||||
// t: typeVar,
|
|
||||||
// unification: new Map(),
|
|
||||||
// e,
|
|
||||||
// depth: 0,
|
|
||||||
// };
|
|
||||||
// return [deepError, {
|
|
||||||
// names: env.names,
|
|
||||||
// nextFreeTypeVar: idx + 1,
|
|
||||||
// typeVars: new Set([...env.typeVars, UNBOUND_SYMBOLS[idx]]),
|
|
||||||
// }];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function removeFocus(state: ExprBlockState): ExprBlockState {
|
|
||||||
// if (state.kind === "input") {
|
|
||||||
// return { ...state, focus: false };
|
|
||||||
// }
|
|
||||||
// else if (state.kind === "call") {
|
|
||||||
// return {
|
|
||||||
// ...state,
|
|
||||||
// fn: removeFocus(state.fn),
|
|
||||||
// input: removeFocus(state.input),
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// else if (state.kind === "lambda") {
|
|
||||||
// return {
|
|
||||||
// ...state,
|
|
||||||
// focus: false,
|
|
||||||
// expr: removeFocus(state.expr),
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// else { // state.kind === "let"
|
|
||||||
// return {
|
|
||||||
// ...state,
|
|
||||||
// focus: false,
|
|
||||||
// value: removeFocus(state.value),
|
|
||||||
// inner: removeFocus(state.inner),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export function addFocusRightMost(state: ExprBlockState) : ExprBlockState {
|
|
||||||
// if (state.kind === "input") {
|
|
||||||
// return { ...state, focus: true };
|
|
||||||
// }
|
|
||||||
// else if (state.kind === "call") {
|
|
||||||
// return {
|
|
||||||
// ... state,
|
|
||||||
// input: addFocusRightMost(state.input),
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// else if (state.kind === "lambda") {
|
|
||||||
// return {
|
|
||||||
// ...state,
|
|
||||||
// expr: addFocusRightMost(state.expr),
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// else { // state.kind === "let"
|
|
||||||
// return {
|
|
||||||
// ...state,
|
|
||||||
// inner: addFocusRightMost(state.inner),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import type { LambdaBlockState } from "../component/expr/LambdaBlock";
|
||||||
import type { LetInBlockState } from "../component/expr/LetInBlock";
|
import type { LetInBlockState } from "../component/expr/LetInBlock";
|
||||||
import { memoize } from "../util/memoize";
|
import { memoize } from "../util/memoize";
|
||||||
|
|
||||||
export interface Environment {
|
export interface StaticEnvironment {
|
||||||
names: any;
|
names: any;
|
||||||
typevars: Set<string>;
|
typevars: Set<string>;
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@ export type Substitutions = Map<string, Type>;
|
||||||
interface TypeInfoCommon {
|
interface TypeInfoCommon {
|
||||||
type: Type;
|
type: Type;
|
||||||
subs: Substitutions;
|
subs: Substitutions;
|
||||||
newEnv: Environment;
|
newEnv: StaticEnvironment;
|
||||||
err?: IncompatibleTypesError;
|
err?: IncompatibleTypesError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ export interface TypeInfoCall extends TypeInfoCommon {
|
||||||
}
|
}
|
||||||
export interface TypeInfoLet extends TypeInfoCommon {
|
export interface TypeInfoLet extends TypeInfoCommon {
|
||||||
kind: "let";
|
kind: "let";
|
||||||
innerEnv: Environment;
|
innerEnv: StaticEnvironment;
|
||||||
value: TypeInfo;
|
value: TypeInfo;
|
||||||
inner: TypeInfo;
|
inner: TypeInfo;
|
||||||
}
|
}
|
||||||
|
|
@ -44,12 +44,12 @@ export interface TypeInfoLambda extends TypeInfoCommon {
|
||||||
kind: "lambda";
|
kind: "lambda";
|
||||||
paramType: Type;
|
paramType: Type;
|
||||||
inner: TypeInfo;
|
inner: TypeInfo;
|
||||||
innerEnv: Environment;
|
innerEnv: StaticEnvironment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TypeInfo = TypeInfoInput | TypeInfoCall | TypeInfoLet | TypeInfoLambda;
|
export type TypeInfo = TypeInfoInput | TypeInfoCall | TypeInfoLet | TypeInfoLambda;
|
||||||
|
|
||||||
export const inferType = memoize(function inferType(s: ExprBlockState, env: Environment): TypeInfo {
|
export const inferType = memoize(function inferType(s: ExprBlockState, env: StaticEnvironment): TypeInfo {
|
||||||
if (s.kind === "input") {
|
if (s.kind === "input") {
|
||||||
return inferTypeInput(s, env);
|
return inferTypeInput(s, env);
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ export const inferType = memoize(function inferType(s: ExprBlockState, env: Envi
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState, env: Environment): TypeInfoInput {
|
export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState, env: StaticEnvironment): TypeInfoInput {
|
||||||
if (s.value.kind === "literal") {
|
if (s.value.kind === "literal") {
|
||||||
const type = {
|
const type = {
|
||||||
Int: Int,
|
Int: Int,
|
||||||
|
|
@ -102,7 +102,7 @@ export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, env: Environment): TypeInfoCall {
|
export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, env: StaticEnvironment): TypeInfoCall {
|
||||||
const fnTypeInfo = inferType(s.fn, env);
|
const fnTypeInfo = inferType(s.fn, env);
|
||||||
const inputEnv = fnTypeInfo.newEnv;
|
const inputEnv = fnTypeInfo.newEnv;
|
||||||
const inputTypeInfo = inferType(s.input, inputEnv);
|
const inputTypeInfo = inferType(s.input, inputEnv);
|
||||||
|
|
@ -162,7 +162,7 @@ export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, e
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, env: Environment): TypeInfoLet {
|
export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, env: StaticEnvironment): TypeInfoLet {
|
||||||
const recursiveTypeInfo = iterateRecursiveType(s.name, s.value, env);
|
const recursiveTypeInfo = iterateRecursiveType(s.name, s.value, env);
|
||||||
// to eval the 'inner' expr, we only need to add our parameter to the environment:
|
// to eval the 'inner' expr, we only need to add our parameter to the environment:
|
||||||
const innerEnv = {
|
const innerEnv = {
|
||||||
|
|
@ -181,7 +181,7 @@ export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, en
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockState, env: Environment): TypeInfoLambda {
|
export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockState, env: StaticEnvironment): TypeInfoLambda {
|
||||||
const recursiveTypeInfo = iterateRecursiveType(s.paramName, s.expr, env);
|
const recursiveTypeInfo = iterateRecursiveType(s.paramName, s.expr, env);
|
||||||
return {
|
return {
|
||||||
kind: "lambda",
|
kind: "lambda",
|
||||||
|
|
@ -192,7 +192,7 @@ 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.
|
// 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.
|
// 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: Environment) {
|
function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: StaticEnvironment) {
|
||||||
let [paramType] = typeUnknown(env);
|
let [paramType] = typeUnknown(env);
|
||||||
const paramTypeVar = paramType.symbol;
|
const paramTypeVar = paramType.symbol;
|
||||||
|
|
||||||
|
|
@ -238,7 +238,7 @@ const highestTypeVar2 = (typevars: Iterable<string>) => {
|
||||||
}
|
}
|
||||||
return highest;
|
return highest;
|
||||||
}
|
}
|
||||||
function rewriteType(type: Type, env: Environment): [Type, Environment] {
|
function rewriteType(type: Type, env: StaticEnvironment): [Type, StaticEnvironment] {
|
||||||
const [recomputed] = recomputeTypeVars([type], highestTypeVar2(env.typevars)+1);
|
const [recomputed] = recomputeTypeVars([type], highestTypeVar2(env.typevars)+1);
|
||||||
const newTypeVars = occurring(recomputed);
|
const newTypeVars = occurring(recomputed);
|
||||||
return [recomputed, {
|
return [recomputed, {
|
||||||
|
|
@ -246,7 +246,7 @@ function rewriteType(type: Type, env: Environment): [Type, Environment] {
|
||||||
typevars: env.typevars.union(newTypeVars),
|
typevars: env.typevars.union(newTypeVars),
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
function typeUnknown(env: Environment): [Type, Environment] {
|
function typeUnknown(env: StaticEnvironment): [Type, StaticEnvironment] {
|
||||||
const type = TYPE_VARS[highestTypeVar2(env.typevars)+1];
|
const type = TYPE_VARS[highestTypeVar2(env.typevars)+1];
|
||||||
const newEnv = {
|
const newEnv = {
|
||||||
names: env.names,
|
names: env.names,
|
||||||
|
|
@ -254,7 +254,7 @@ function typeUnknown(env: Environment): [Type, Environment] {
|
||||||
};
|
};
|
||||||
return [type, newEnv];
|
return [type, newEnv];
|
||||||
}
|
}
|
||||||
function rewriteInferredType(type: Type, env: Environment): [Type, Environment] {
|
function rewriteInferredType(type: Type, env: StaticEnvironment): [Type, StaticEnvironment] {
|
||||||
const substitutions = new Map();
|
const substitutions = new Map();
|
||||||
const newTypeVars = new Set(env.typevars);
|
const newTypeVars = new Set(env.typevars);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue