open Belt
open Data
let _genSymCtr = ref(0)
let genSym = (~prefix="var", ()): string => {
let num = _genSymCtr.contents
_genSymCtr := num + 1
`${prefix}${Int.toString(num)}`
}
let rec isSubtype = (ctx: context, tyA: typ, tyB: typ): result<context, string> => {
switch (tyA, tyB) {
| (_, _) => failwith("TODO")
}
}
and typecheck = (ctx: context, term: term, typ: typ): result<context, string> => {
switch (term, typ) {
// 1I rule
| (Unit, Unit) => Ok(ctx)
// ∀I rule
| (e, Polytype(x, tyA)) => failwith("TODO")
// →I rule
| (Lam(x, e), Arrow(tyA, tyB)) =>
let augmentedCtx = ctx->List.add(TermAnnot(x, tyA))
typecheck(augmentedCtx, e, tyB)->Result.map(_ => ctx)
// Sub rule
// TODO: Subtyping rule???
| (Var(_) | Lam(_, _) | App(_, _) | Annot(_, _), _) => failwith("TODO")
| _ => Error("could not resolve")
}
}
and synthesize = (ctx: context, term: term): result<(typ, context), string> => {
switch term {
// Var rule
| Var(name) => Ok(lookupTypeVariable(ctx, name), ctx)
// 1I⇒ rule
| Unit => Ok(Unit, ctx)
// Anno rule
| Annot(e, tyA) => typecheck(ctx, e, tyA)->Result.map(ctx => (tyA, ctx))
// →I⇒ rule
| Lam(x, e) =>
// TODO: Check x and e
let exA = genSym(~prefix="ex", ())
let exB = genSym(~prefix="ex", ())
Ok(Arrow(Existential(exA), Existential(exB)), ctx)
// →E rule
| App(e1, e2) => failwith("TODO")
}
}
and appSynthesize = (ctx: context, funTy: typ, term: term): result<(typ, context), string> => {
switch (funTy, term) {
// →App rule
| (Arrow(tyA, tyC), e) => typecheck(ctx, e, tyA)->Result.map(_ => (tyC, ctx))
// ∀App rule
| (Polytype(a, tyA), e) => failwith("TODO")
// âApp rule
| (Existential(_), _) => failwith("TODO")
| (Unit | Var(_), _) => Error("trying to appSynthesize with a non-function type")
}
}
// Figure 8. Applying a context, as a substitution, to a type
let rec applyContext = (ctx: context, typ: typ): typ => {
switch typ {
| Unit => Unit
| Var(x) => Var(x)
| Existential(_) => failwith("TODO")
| Polytype(_) => failwith("TODO")
| Arrow(a, b) => Arrow(applyContext(ctx, a), applyContext(ctx, b))
}
}