structural equality, booleans, relational barriers for propagation

This commit is contained in:
Yura Dupyn 2026-02-18 00:29:17 +01:00
parent 6cca0d17a1
commit 115b457173
4 changed files with 167 additions and 18 deletions

View file

@ -75,3 +75,59 @@ export namespace Env {
}
}
export function equals(v1: Value, v2: Value): boolean {
if (v1 === v2) return true; // Reference equality optimization
if (v1.tag !== v2.tag) return false;
switch (v1.tag) {
case "number":
return v1.value === (v2 as Extract<Value, { tag: "number" }>).value;
case "string":
return v1.value === (v2 as Extract<Value, { tag: "string" }>).value;
case "tag":
return v1.tag_name === (v2 as Extract<Value, { tag: "tag" }>).tag_name;
case "tagged": {
const other = v2 as Extract<Value, { tag: "tagged" }>;
return v1.tag_name === other.tag_name && equals(v1.value, other.value);
}
case "tuple": {
const other = v2 as Extract<Value, { tag: "tuple" }>;
if (v1.values.length !== other.values.length) return false;
for (let i = 0; i < v1.values.length; i++) {
if (!equals(v1.values[i], other.values[i])) return false;
}
return true;
}
case "record": {
const other = v2 as Extract<Value, { tag: "record" }>;
if (v1.fields.size !== other.fields.size) return false;
for (const [key, val1] of v1.fields) {
const val2 = other.fields.get(key);
if (val2 === undefined || !equals(val1, val2)) return false;
}
return true;
}
case "closure":
// Philosophical/Mathematical barrier: throw error as requested
throw ThrownRuntimeError.error({
tag: "ClosureEqualityComparison",
value0: v1.closure,
value1: v2,
});
}
}
// Canonical bools are:
// - True is `#T`
// - False is `#F`
// TODO: This is not a great design. Probably introducing completely new values would be better.
export function forceBool(value: Value): boolean {
if (value.tag === "tag") {
if (value.tag_name === "T") return true;
if (value.tag_name === "F") return false;
}
throw ThrownRuntimeError.error({
tag: "NotABoolean",
value
});
}