Typeclasses

This commit is contained in:
Dustin Swan 2026-03-26 21:38:32 -06:00
parent a4daf88085
commit 8020f9d1a0
No known key found for this signature in database
GPG key ID: 30D46587E2100467
6 changed files with 123 additions and 19 deletions

View file

@ -1,4 +1,4 @@
import type { AST, TypeAST, Pattern, Definition, TypeDefinition } from './ast'
import type { AST, TypeAST, Pattern, Definition, TypeDefinition, ClassDefinition, InstanceDeclaration } from './ast'
import { prettyPrintType } from './ast'
// Map type var names to their types
@ -155,6 +155,24 @@ function infer(expr: AST, env: TypeEnv, subst: Subst): TypeAST | null {
if (err) warn(err, arg);
current = applySubst(current.result, subst);
}
// Check typeclass constraints
if (expr.func.kind === 'variable') {
const constraint = moduleConstraints.get(expr.func.name);
if (constraint) {
const argType = infer(expr.args[0], env, subst);
if (argType) {
const resolved = applySubst(argType, subst);
if (resolved.kind === 'type-name') {
const instances = moduleInstances.get(constraint.className);
if (!instances || !instances.has(resolved.name)) {
warn(`No instance ${constraint.className} ${resolved.name}`, expr);
}
}
}
}
}
return current;
}
@ -309,9 +327,28 @@ function bindPattern(pattern: Pattern, type: TypeAST, env: TypeEnv, subst: Subst
}
}
export function typecheck(defs: Definition[], typeDefs: TypeDefinition[] = []) {
let moduleConstraints = new Map<string, { param: string, className: string }>();
let moduleInstances = new Map<string, Set<string>>();
export function typecheck(defs: Definition[], typeDefs: TypeDefinition[] = [], classDefs: ClassDefinition[] = [], instances: InstanceDeclaration[] = []) {
const env: TypeEnv = new Map();
// Register instances as a lookup: className -> Set of type names
const instanceMap = new Map<string, Set<string>>();
for (const inst of instances) {
if (!instanceMap.has(inst.className)) instanceMap.set(inst.className, new Set());
instanceMap.get(inst.className)!.add(inst.typeName);
}
moduleInstances = instanceMap;
// Register class methods with constraints in env
for (const cls of classDefs) {
for (const method of cls.methods) {
env.set(method.name, method.type);
moduleConstraints.set(method.name, { param: cls.param, className: cls.name });
}
}
// Register constructors
for (const td of typeDefs) {
const resultType: TypeAST = td.params.length === 0