diff --git a/src/component/app/App.tsx b/src/component/app/App.tsx index 15919f3..f01a7ec 100644 --- a/src/component/app/App.tsx +++ b/src/component/app/App.tsx @@ -9,7 +9,7 @@ import { biggerExample, emptySet, factorial, higherOrder, higherOrder2Params, in import './App.css'; import { evalExpr } from '../../eval/eval'; import { Value } from '../other/Value'; -import { Type, TypeInfoBlock } from '../other/Type'; +import { Type } from '../other/Type'; const examples: [string, ExprBlockState][] = [ @@ -190,7 +190,7 @@ export function App() { t: typeInfo.type, }}/> :: - + diff --git a/src/component/app/configurations.ts b/src/component/app/configurations.ts index 4d24127..32befcb 100644 --- a/src/component/app/configurations.ts +++ b/src/component/app/configurations.ts @@ -75,7 +75,7 @@ export const tripleFunctionCallEditorState: ExprBlockState = { }, }; -export const biggerExample: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"input","text":"myList","value":{"kind":"name"}},"name":"myListInc","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"myList","value":{"kind":"name"}}},"input":{"kind":"input","text":"inc","value":{"kind":"name"},"focus":false}}},"input":{"kind":"input","text":"id","value":{"kind":"name"},"focus":true}}},"name":"myList","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"list.emptyList","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"input","text":"2","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"input","text":"3","value":{"kind":"literal","type":"Int"},"focus":false}}},"name":"id","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}}},"name":"inc","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"addInt","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":true}}}}; +export const biggerExample: ExprBlockState = {"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"let","focus":false,"inner":{"kind":"input","text":"","value":{"kind":"gibberish"},"focus":false},"name":"myListInc","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.map","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"myList","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"inc","value":{"kind":"name"},"focus":false}}},"input":{"kind":"input","text":"id","value":{"kind":"name"},"focus":true}}},"name":"myList","value":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"list.push","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"list.emptyList","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"input","text":"2","value":{"kind":"literal","type":"Int"},"focus":false}}},"input":{"kind":"input","text":"3","value":{"kind":"literal","type":"Int"},"focus":false}}},"name":"id","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}}},"name":"inc","value":{"kind":"lambda","focus":false,"paramName":"x","expr":{"kind":"call","fn":{"kind":"call","fn":{"kind":"input","text":"addInt","value":{"kind":"name"},"focus":false},"input":{"kind":"input","text":"x","value":{"kind":"name"},"focus":false}},"input":{"kind":"input","text":"1","value":{"kind":"literal","type":"Int"},"focus":true}}}}; export const lambda2Params: ExprBlockState = { "kind": "let", diff --git a/src/component/expr/CallBlock.tsx b/src/component/expr/CallBlock.tsx index e5c77b5..6dfda9d 100644 --- a/src/component/expr/CallBlock.tsx +++ b/src/component/expr/CallBlock.tsx @@ -5,7 +5,7 @@ import { EnvContext } from "../../context/EnvContext"; import { GlobalContext } from "../../context/GlobalContext"; import { type StaticEnvironment, type TypeInfoCall } from "../../eval/infer_type"; import { getActions } from "../app/actions"; -import { Type, TypeInfoBlock } from "../other/Type"; +import { Type } from "../other/Type"; import "./CallBlock.css"; import { ExprBlock, type ExprBlockState, type SetStateFn, type State2Props } from "./ExprBlock"; @@ -67,7 +67,7 @@ export function CallBlock(props: CallBlockProps) { {/* { (resolved.kind === "error") && resolved.e.toString() || (resolved.kind === "value") && || "unknown" } */} - :: + :: diff --git a/src/component/expr/ExprBlock.css b/src/component/expr/ExprBlock.css index 81dc5b3..e579fcd 100644 --- a/src/component/expr/ExprBlock.css +++ b/src/component/expr/ExprBlock.css @@ -1,26 +1,10 @@ .editor { - padding: 2px; - position: relative; + padding: 2px;; } .editor.error { border: 1px solid red; display: inline-block; } - -.errorMessage { - display: none; - position: absolute; - color: darkred; - background-color: pink; - margin-top: 4px; - z-index: 10; -} - -.editor:hover > .errorMessage { - display: block; - /* z-index: 9999; */ -} - .editor.unknown { border: 1px dashed dodgerblue; display: inline-block; @@ -35,6 +19,37 @@ background-color: transparent; } +.typeSignature { + display: inline-block; + /* z-index: 1; */ + position: relative; +} + +.typeSignature.gotDebug { + background-color: gold; + padding: 2px; +} + +.typeDebug { + display: none; +} + +.typeSignature:hover > .typeDebug { + display: inline-block; + position: absolute; + white-space-collapse: preserve; + width: max-content; + background-color: #d2ebf1e0; + color: black; + font-family: var(--my-monospace-font); + padding: 4px; + z-index: 1000; +} + +.editor:hover > .typeSignature { + display: inline-block; +} + .keyword { color: blue; font-weight: bold; diff --git a/src/component/expr/ExprBlock.tsx b/src/component/expr/ExprBlock.tsx index f048a65..834d06e 100644 --- a/src/component/expr/ExprBlock.tsx +++ b/src/component/expr/ExprBlock.tsx @@ -51,13 +51,12 @@ export function ExprBlock(props: ExprBlockProps) { const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) => [shortcut, (e) => { e.preventDefault(); action(); }])) - console.log(props.typeInfo.err); return {renderBlock[props.state.kind]()} - {(props.typeInfo.err !== undefined) && - (
- {props.typeInfo.err.message} -
)} + {props.typeInfo.err && +
+ {props.typeInfo.err.message.split('\n')[1]} +
}
- :: + :: } @@ -200,7 +200,7 @@ function Suggestion({ setI, j, onSelect, highlighted, suggestion: [priority, typ onMouseEnter={onMouseEnter(j)} onMouseDown={onMouseDown(j)}> ({priority}) ({kind}) {text} :: - ; + } const SuggestionMemo = memo(Suggestion); \ No newline at end of file diff --git a/src/component/expr/LetInBlock.tsx b/src/component/expr/LetInBlock.tsx index 1dce010..ab7b787 100644 --- a/src/component/expr/LetInBlock.tsx +++ b/src/component/expr/LetInBlock.tsx @@ -4,7 +4,7 @@ import { EnvContext } from "../../context/EnvContext"; import { GlobalContext } from "../../context/GlobalContext"; import { type TypeInfoLet } from "../../eval/infer_type"; import { Input } from "../other/Input"; -import { Type, TypeInfoBlock } from "../other/Type"; +import { Type } from "../other/Type"; import { ExprBlock, type ExprBlockState, type State2Props } from "./ExprBlock"; import "./LetInBlock.css"; @@ -50,7 +50,7 @@ function DeclColumns({state, setState, score, typeInfo}) { onTextChange={name => setState(state => ({...state, name}))} extraHandlers={{}} /> - :: + ::  =  diff --git a/src/component/other/Type.css b/src/component/other/Type.css index 2bd3da4..c87040c 100644 --- a/src/component/other/Type.css +++ b/src/component/other/Type.css @@ -70,29 +70,3 @@ 50% { opacity:0; } 100% { opacity:1; } } - - -.typeSignature { - display: inline-block; - position: relative; -} - -.typeDebug { - display: none; -} - -.typeSignature:hover > .typeDebug { - display: inline-block; - position: absolute; - white-space-collapse: preserve; - width: max-content; - background-color: #d2ebf1e0; - color: black; - font-family: var(--my-monospace-font); - padding: 4px; - z-index: 100; -} - -.editor:hover > .typeSignature { - display: inline-block; -} diff --git a/src/component/other/Type.tsx b/src/component/other/Type.tsx index 9c4d01d..6a8bb59 100644 --- a/src/component/other/Type.tsx +++ b/src/component/other/Type.tsx @@ -1,16 +1,7 @@ -import { getHumanReadableName, getSymbol, prettySS, symbolDict, symbolDictIterator, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSetIterator, symbolSum } from "dope2"; +import { getHumanReadableName, getSymbol, symbolDict, symbolDictIterator, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSetIterator, symbolSum } from "dope2"; import "./Type.css"; import { ValueUnknown } from "./Value"; -import type { TypeInfo } from "../../eval/infer_type"; - -export function TypeInfoBlock({typeInfo}: {typeInfo: TypeInfo}) { - return - -
- {prettySS(typeInfo.subs)} -
; -} export function Type({type}) { if (type === undefined) { diff --git a/src/eval/eval.ts b/src/eval/eval.ts index 86d9578..0eb4955 100644 --- a/src/eval/eval.ts +++ b/src/eval/eval.ts @@ -60,7 +60,7 @@ export function evalLet(s: LetInBlockState, env: DynamicEnvironment): EvalResult const valueEnv = { names: trie.insert(env.names)(s.name)({ recursive: true, - i: () => { try { return value; } catch (e) {} }, + i: () => value, }), }; const value = evalExpr(s.value, valueEnv); diff --git a/src/eval/infer_type.ts b/src/eval/infer_type.ts index dc77b50..e7d8a8e 100644 --- a/src/eval/infer_type.ts +++ b/src/eval/infer_type.ts @@ -1,4 +1,4 @@ -import { Double, eqType, fnType, getHumanReadableName, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, prettySS, prettyT, recomputeTypeVars, substitute, SubstitutionCycle, trie, TYPE_VARS, UNBOUND_SYMBOLS, unify } from "dope2"; +import { Double, eqType, fnType, IncompatibleTypesError, Int, mergeSubstitutionsN, occurring, 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; // mapping from name to type - typevars: Set; // set of type variables currently in use + names: any; + typevars: Set; } export interface Type { @@ -98,7 +98,7 @@ export const inferTypeInput = memoize(function inferTypeInput(s: InputBlockState type, subs: new Map(), newEnv, - err: new Error(`'${s.text}' not found`), + err: new Error("Gibberish"), } }); @@ -163,15 +163,10 @@ export const inferTypeCall = memoize(function inferTypeCall(s: CallBlockState, e }); export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, env: StaticEnvironment): TypeInfoLet { - console.log('inferTypeLet..', env.typevars); - const recursiveTypeInfo = iterateRecursiveType(s.name, s.value, env, true); - console.log('end inferTypeLet.'); - console.log({recursiveTypeInfo}); + 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 = { - names: trie.insert(env.names)(s.name)({ - t: recursiveTypeInfo.inner.type, - }), + names: trie.insert(env.names)(s.name)({kind: "value", t: recursiveTypeInfo.paramType}), typevars: env.typevars, }; const innerTypeInfo = inferType(s.inner, innerEnv); @@ -187,9 +182,7 @@ export const inferTypeLet = memoize(function inferTypeLet(s: LetInBlockState, en }); export const inferTypeLambda = memoize(function inferTypeLambda(s: LambdaBlockState, env: StaticEnvironment): TypeInfoLambda { - console.log('inferTypeLambda..', env.typevars); - const recursiveTypeInfo = iterateRecursiveType(s.paramName, s.expr, env, false); - console.log('end inferTypeLambda.'); + const recursiveTypeInfo = iterateRecursiveType(s.paramName, s.expr, env); return { kind: "lambda", type: fnType(_ => recursiveTypeInfo.paramType)(_ => recursiveTypeInfo.inner.type), @@ -199,36 +192,21 @@ 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, paramIsInner: boolean) { +function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: StaticEnvironment) { let [paramType] = typeUnknown(env); const paramTypeVar = paramType.symbol; - let iterations = 0; + let iterations = 1; 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), }; - 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, @@ -238,11 +216,17 @@ function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: Stat innerEnv, }; } - - if ((iterations++) == 10) { + if ((iterations++) == 100) { throw new Error("too many iterations! something's wrong!"); } - paramType = inferredPType2; // next iteration + // 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; } } @@ -275,7 +259,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 (newTypeVars.has(UNBOUND_SYMBOLS[i])) { + while (env.typevars.has(UNBOUND_SYMBOLS[i])) { i++; } if (!env.typevars.has(o)) {