nicer looking types

This commit is contained in:
Joeri Exelmans 2025-05-27 13:20:09 +02:00
parent 428e8cd298
commit abd3bd3645
4 changed files with 25 additions and 9 deletions

View file

@ -14,6 +14,10 @@
background-color: pink;
margin-top: 4px;
z-index: 10;
font-family: var(--my-monospace-font);
white-space-collapse: preserve;
font-size: 10pt;
width: max-content;
}
.editor:hover > .errorMessage {

View file

@ -50,13 +50,11 @@ export function ExprBlock(props: ExprBlockProps) {
const actions = getActions(globalContext, props.setState);
const extraHandlers = Object.fromEntries(Object.entries(actions).map(([shortcut, action]) =>
[shortcut, (e) => { e.preventDefault(); action(); }]))
console.log(props.typeInfo.err);
return <span className={"editor" + (props.typeInfo.err ? " error" : "")}>
{renderBlock[props.state.kind]()}
{(props.typeInfo.err !== undefined) &&
(<div className="errorMessage">
{props.typeInfo.err.message}
{props.typeInfo.err.message.trim()}
</div>)}
<Input
placeholder="<c>"

View file

@ -50,6 +50,7 @@ function DeclColumns({state, setState, score, typeInfo}) {
onTextChange={name => setState(state => ({...state, name}))}
extraHandlers={{}}
/>
<br/>
:: <TypeInfoBlock typeInfo={typeInfo.value} />
</span>
<span className="keyword column">&nbsp;=&nbsp;</span>

View file

@ -163,10 +163,7 @@ 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});
// to eval the 'inner' expr, we only need to add our parameter to the environment:
const innerEnv = {
names: trie.insert(env.names)(s.name)({
@ -187,9 +184,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.');
return {
kind: "lambda",
type: fnType(_ => recursiveTypeInfo.paramType)(_ => recursiveTypeInfo.inner.type),
@ -199,7 +194,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: StaticEnvironment, paramIsInner: boolean) {
function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: StaticEnvironment, isLet: boolean) {
let [paramType] = typeUnknown(env);
const paramTypeVar = paramType.symbol;
@ -230,6 +225,24 @@ function iterateRecursiveType(paramName: string, expr: ExprBlockState, env: Stat
// console.log("-----------------");
if (eqType(inferredPType2)(paramType)) {
// The following part is not really necessary, but it makes types look nicer (start at the lowest possible typevar) for non-recursive let-expressions.
if (isLet && occurring(paramType).intersection(occurring(innerTypeInfo.type)).size === 0) {
const innerEnv = {
names: trie.insert(env.names)(paramName)({
kind: "unknown",
t: innerTypeInfo.type,
}),
typevars: env.typevars.union(occurring(innerTypeInfo.type) as Set<string>),
};
const innerTypeInfoWithoutTypeVar = inferType(expr, innerEnv);
return {
subs: innerTypeInfo.subs,
newEnv: env,
paramType: innerTypeInfoWithoutTypeVar.type,
inner: innerTypeInfoWithoutTypeVar,
innerEnv,
}
}
return {
subs: subsWithoutPType,
newEnv,