wrote interpreter (independent of type inferencer)

This commit is contained in:
Joeri Exelmans 2025-05-26 15:34:50 +02:00
parent 2279c54229
commit 32bdc23ea7
6 changed files with 104 additions and 602 deletions

View file

@ -7,6 +7,7 @@ import { actionShortcuts } from './actions';
import { biggerExample, emptySet, factorial, higherOrder, higherOrder2Params, inc, initialEditorState, lambda2Params, nonEmptyEditorState, pushBool, setOfListOfBool, tripleFunctionCallEditorState } from "./configurations";
import './App.css';
import { evalExpr } from '../../eval/eval';
const examples: [string, ExprBlockState][] = [
@ -181,6 +182,7 @@ export function App() {
}}
typeInfo={typeInfo}
/>
={evalExpr(currentState, extendedEnv)}
</GlobalContext>
</main>

View file

@ -3,7 +3,7 @@ import { useContext } from "react";
import { CallContext } from "../../context/CallContext";
import { EnvContext } from "../../context/EnvContext";
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 { Type } from "../other/Type";
import "./CallBlock.css";
@ -22,7 +22,7 @@ export interface CallBlockProps<
typeInfo: TypeInfoCall;
}
function nestedFnProperties({state, setState, score, typeInfo}: CallBlockProps, env: Environment) {
function nestedFnProperties({state, setState, score, typeInfo}: CallBlockProps, env: StaticEnvironment) {
const setFn = (callback: SetStateFn) => {
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};
}
function nestedInputProperties({state, setState, score, typeInfo}: CallBlockProps, env: Environment) {
function nestedInputProperties({state, setState, score, typeInfo}: CallBlockProps, env: StaticEnvironment) {
const setInput = (callback: SetStateFn) => {
setState(state => ({...state, input: callback(state.input)}));
};

View file

@ -5,7 +5,7 @@ import { trie } from "dope2";
import { CallContext } from "../../context/CallContext";
import { EnvContext } from "../../context/EnvContext";
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 { Input } from "../other/Input";
import { Type as TypeBlock } from "../other/Type";
@ -46,9 +46,11 @@ const attemptLiterals = [
const computeSuggestions = (
text: string,
env: Environment,
env: StaticEnvironment,
score: InputBlockProps['score'],
): PrioritizedSuggestionType[] => {
const startTime = performance.now();
const ls = [
...attemptLiterals
.filter(([_, test]) => test(text))
@ -69,9 +71,17 @@ const computeSuggestions = (
},
})),
];
const midTime = performance.now();
// 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);
const endTime = performance.now();
console.log(`computing suggestions took ${midTime-startTime} + ${endTime - midTime}`);
return result;
}
export function InputBlock({ state, setState, score, onCancel, typeInfo }: InputBlockProps) {

View file

@ -1,14 +1,14 @@
import { createContext } from "react";
import { getDefaultTypeParser, module2Env, ModuleStd } from "dope2";
// 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 functionWith4Params = i => j => k => l => i+j+k+l;
const mkType = getDefaultTypeParser();
export const extendedEnv: Environment = {
export const extendedEnv: StaticEnvironment = {
names: module2Env(ModuleStd.concat([
["true", {i: true, t: mkType("Bool")}],
["false", {i: false, t: mkType("Bool")}],

View file

@ -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";
// import type { InputValueType } from "./InputBlock";
export interface DynamicEnvironment {
names: any;
}
// const IS_DEV = (import.meta.env.MODE === "development");
// const VERBOSE = true && IS_DEV;
export type EvalResult = any;
// export interface Environment {
// names: any;
// nextFreeTypeVar: number;
// typeVars: Set<string>;
// }
export function evalExpr(s: ExprBlockState, env: DynamicEnvironment): EvalResult {
if (s.kind === "input") {
return evalInput(s, env);
}
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 {
// symbol: string;
// params: any[];
// };
export function evalInput(s: InputBlockState, env: DynamicEnvironment): EvalResult {
if (s.value.kind === "literal") {
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 {
// kind: "error";
// e: Error;
// depth: number;
// t: Type;
// unification: Unification;
// }
export function evalLet(s: LetInBlockState, env: DynamicEnvironment): EvalResult {
const valueEnv = {
names: trie.insert(env.names)(s.name)({
recursive: true,
i: () => value,
}),
};
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 interface Dynamic {
// kind: "value",
// i: any;
// t: Type;
// unification: Unification;
// };
// 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),
// }
// }
// }
export function evalLambda(s: LambdaBlockState, env: DynamicEnvironment): EvalResult {
const fn = x => {
const innerEnv = {
names: trie.insert(env.names)(s.paramName)({i: x})
};
return evalExpr(s.expr, innerEnv);
};
return fn;
}

View file

@ -7,7 +7,7 @@ import type { LambdaBlockState } from "../component/expr/LambdaBlock";
import type { LetInBlockState } from "../component/expr/LetInBlock";
import { memoize } from "../util/memoize";
export interface Environment {
export interface StaticEnvironment {
names: any;
typevars: Set<string>;
}
@ -22,7 +22,7 @@ export type Substitutions = Map<string, Type>;
interface TypeInfoCommon {
type: Type;
subs: Substitutions;
newEnv: Environment;
newEnv: StaticEnvironment;
err?: IncompatibleTypesError;
}
@ -36,7 +36,7 @@ export interface TypeInfoCall extends TypeInfoCommon {
}
export interface TypeInfoLet extends TypeInfoCommon {
kind: "let";
innerEnv: Environment;
innerEnv: StaticEnvironment;
value: TypeInfo;
inner: TypeInfo;
}
@ -44,12 +44,12 @@ export interface TypeInfoLambda extends TypeInfoCommon {
kind: "lambda";
paramType: Type;
inner: TypeInfo;
innerEnv: Environment;
innerEnv: StaticEnvironment;
}
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") {
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") {
const type = {
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 inputEnv = fnTypeInfo.newEnv;
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);
// to eval the 'inner' expr, we only need to add our parameter to the environment:
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);
return {
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.
// 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);
const paramTypeVar = paramType.symbol;
@ -238,7 +238,7 @@ const highestTypeVar2 = (typevars: Iterable<string>) => {
}
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 newTypeVars = occurring(recomputed);
return [recomputed, {
@ -246,7 +246,7 @@ function rewriteType(type: Type, env: Environment): [Type, Environment] {
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 newEnv = {
names: env.names,
@ -254,7 +254,7 @@ function typeUnknown(env: Environment): [Type, Environment] {
};
return [type, newEnv];
}
function rewriteInferredType(type: Type, env: Environment): [Type, Environment] {
function rewriteInferredType(type: Type, env: StaticEnvironment): [Type, StaticEnvironment] {
const substitutions = new Map();
const newTypeVars = new Set(env.typevars);
let i = 0;