This commit is contained in:
Michael Zhang 2023-11-04 13:50:45 -05:00
parent f88f349934
commit 2863e10113
3 changed files with 104 additions and 45 deletions

View file

@ -2,7 +2,7 @@ use anyhow::{bail, Result};
use crate::DEPTH; use crate::DEPTH;
use crate::data::FreeVar; use crate::data::{FreeVar, Monotype};
use crate::{ use crate::{
data::{Context, ContextEntry, Term, Type}, data::{Context, ContextEntry, Term, Type},
gensym::gensym_prefix, gensym::gensym_prefix,
@ -10,12 +10,11 @@ use crate::{
// Figure 8. Applying a context, as a substitution, to a type // Figure 8. Applying a context, as a substitution, to a type
#[trace]
pub fn app_ctx(ctx: &Context, ty: &Type) -> Result<Type> { pub fn app_ctx(ctx: &Context, ty: &Type) -> Result<Type> {
match ty { match ty {
Type::Existential(a) => match ctx.lookup_existential(a) { Type::Existential(a) => match ctx.lookup_existential(a) {
Some(Some(m)) => Ok(m.into_poly()), Some((_, Some(m))) => Ok(m.into_poly()),
Some(None) => Ok(Type::Existential(a.clone())), Some((_, None)) => Ok(Type::Existential(a.clone())),
None => bail!("existential variable {a} doesn't exist in context"), None => bail!("existential variable {a} doesn't exist in context"),
}, },
@ -38,22 +37,12 @@ pub fn subtype(ctx: &Context, a: &Type, b: &Type) -> Result<Context> {
(Type::Unit, Type::Unit) => Ok(ctx.clone()), (Type::Unit, Type::Unit) => Ok(ctx.clone()),
// <:Var rule // <:Var rule
(Type::Var(x), Type::Var(y)) if x == y => { (Type::Var(x), Type::Var(y)) if x == y && ctx.lookup_type(x).is_some() => Ok(ctx.clone()),
// Ensure that the name exists in the context
if ctx.lookup_type(x).is_none() {
bail!("name {x} not in context");
}
Ok(ctx.clone())
}
// <:Exvar rule // <:Exvar rule
(Type::Existential(x), Type::Existential(y)) if x == y => { (Type::Existential(x), Type::Existential(y))
// Ensure that the name exists in the context if x == y && ctx.lookup_existential(x).is_some() =>
if ctx.lookup_existential(x).is_none() { {
bail!("name {x} not in context");
}
Ok(ctx.clone()) Ok(ctx.clone())
} }
@ -90,9 +79,21 @@ pub fn instantiate_left(ctx: &Context, a: &str, ty_a: &Type) -> Result<Context>
match ty_a { match ty_a {
// InstLReach rule // InstLReach rule
Type::Existential(b) if ctx.has_existential(a) && ctx.has_existential(b) => { Type::Existential(b) if ctx.has_existential(a) && ctx.has_existential(b) => {
// TODO: WTF? let mut new_ctx = ctx.clone();
todo!() {
let b_entry = new_ctx
.0
.iter_mut()
.find(|entry| matches!(entry, ContextEntry::ExistentialVar(x) if x == b))
.expect("should exist");
*b_entry =
ContextEntry::ExistentialSolved(b.to_owned(), Monotype::Existential(a.to_owned()));
}
Ok(new_ctx)
} }
Type::Existential(b) => todo!(), Type::Existential(b) => todo!(),
Type::Unit => todo!(), Type::Unit => todo!(),
@ -134,13 +135,11 @@ pub fn typecheck(ctx: &Context, term: &Term, ty: &Type) -> Result<Context> {
pub fn synthesize(ctx: &Context, term: &Term) -> Result<(Type, Context)> { pub fn synthesize(ctx: &Context, term: &Term) -> Result<(Type, Context)> {
match term { match term {
// Var rule // Var rule
Term::Var(name) => { Term::Var(name) if ctx.has_type(name) => {
let ty = match ctx.lookup_type(name) { let (_, ty) = ctx.lookup_type(name).unwrap();
Some(v) => v,
None => bail!("could not find name {name}"),
};
Ok((ty, ctx.clone())) Ok((ty, ctx.clone()))
} }
Term::Var(name) => bail!("could not find name {name}"),
// 1I⇒ rule // 1I⇒ rule
Term::Unit => Ok((Type::Unit, ctx.clone())), Term::Unit => Ok((Type::Unit, ctx.clone())),

View file

@ -1,8 +1,11 @@
use std::hash::Hash; use std::{
fmt::{self, write},
hash::Hash,
};
use im::{HashSet, Vector}; use im::{HashSet, Vector};
#[derive(Debug, Clone)] #[derive(Clone)]
pub enum Term { pub enum Term {
Unit, Unit,
Var(String), Var(String),
@ -11,7 +14,19 @@ pub enum Term {
Annot(Box<Term>, Type), Annot(Box<Term>, Type),
} }
#[derive(Debug, Clone)] impl fmt::Debug for Term {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unit => write!(f, "unit"),
Self::Var(arg0) => write!(f, "`{}", arg0),
Self::Lam(arg0, arg1) => write!(f, "λ{}.{:?}", arg0, arg1),
Self::App(arg0, arg1) => write!(f, "({:?} · {:?})", arg0, arg1),
Self::Annot(arg0, arg1) => write!(f, "({:?} : {:?})", arg0, arg1),
}
}
}
#[derive(Clone)]
pub enum Type { pub enum Type {
Unit, Unit,
Var(String), Var(String),
@ -20,6 +35,18 @@ pub enum Type {
Arrow(Box<Type>, Box<Type>), Arrow(Box<Type>, Box<Type>),
} }
impl fmt::Debug for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unit => write!(f, "𝟙"),
Self::Var(arg0) => write!(f, "`α-{}", arg0),
Self::Existential(arg0) => write!(f, "`α\u{0302}-{}", arg0),
Self::Polytype(arg0, arg1) => write!(f, "(∀{}.{:?})", arg0, arg1),
Self::Arrow(arg0, arg1) => write!(f, "({:?} → {:?})", arg0, arg1),
}
}
}
impl Type { impl Type {
pub fn into_mono(&self) -> Option<Monotype> { pub fn into_mono(&self) -> Option<Monotype> {
match self { match self {
@ -90,7 +117,7 @@ impl Monotype {
} }
} }
#[derive(Debug, Clone)] #[derive(Clone)]
pub enum ContextEntry { pub enum ContextEntry {
TypeVar(String), TypeVar(String),
TermAnnot(String, Type), TermAnnot(String, Type),
@ -99,6 +126,18 @@ pub enum ContextEntry {
Marker(String), Marker(String),
} }
impl fmt::Debug for ContextEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TypeVar(arg0) => write!(f, "`{}", arg0),
Self::TermAnnot(arg0, arg1) => write!(f, "{} : {:?}", arg0, arg1),
Self::ExistentialVar(arg0) => write!(f, "`{}", arg0),
Self::ExistentialSolved(arg0, arg1) => write!(f, "{:?} = {:?}", arg0, arg1),
Self::Marker(arg0) => write!(f, "▶{}", arg0),
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum CompleteContextEntry { pub enum CompleteContextEntry {
TypeVar(String), TypeVar(String),
@ -107,8 +146,19 @@ pub enum CompleteContextEntry {
Marker(String), Marker(String),
} }
#[derive(Debug, Clone, Default)] #[derive(Clone, Default)]
pub struct Context(Vector<ContextEntry>); pub struct Context(pub(crate) Vector<ContextEntry>);
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Γ")?;
for rule in self.0.iter() {
f.write_str(", ")?;
rule.fmt(f)?;
}
Ok(())
}
}
impl Context { impl Context {
pub fn add(&self, entries: Vec<ContextEntry>) -> Context { pub fn add(&self, entries: Vec<ContextEntry>) -> Context {
@ -119,12 +169,16 @@ impl Context {
new_ctx new_ctx
} }
/// Looks up a polytype by name /// Looks up a polytype by name, also returning an index
pub fn lookup_type(&self, name: impl AsRef<str>) -> Option<Type> { pub fn lookup_type(&self, name: impl AsRef<str>) -> Option<(usize, Type)> {
self.0.iter().find_map(|entry| match entry { self
ContextEntry::TermAnnot(n, t) if n == name.as_ref() => Some(t.clone()), .0
_ => None, .iter()
}) .enumerate()
.find_map(|(i, entry)| match entry {
ContextEntry::TermAnnot(n, t) if n == name.as_ref() => Some((i, t.clone())),
_ => None,
})
} }
#[inline] #[inline]
@ -136,12 +190,16 @@ impl Context {
/// - Returns Some(Some(t)) if solved /// - Returns Some(Some(t)) if solved
/// - Returns Some(None) if exists but unsolved /// - Returns Some(None) if exists but unsolved
/// - Returns None if not found /// - Returns None if not found
pub fn lookup_existential(&self, name: impl AsRef<str>) -> Option<Option<Monotype>> { pub fn lookup_existential(&self, name: impl AsRef<str>) -> Option<(usize, Option<Monotype>)> {
self.0.iter().find_map(|entry| match entry { self
ContextEntry::ExistentialVar(n) if n == name.as_ref() => Some(None), .0
ContextEntry::ExistentialSolved(n, t) if n == name.as_ref() => Some(Some(t.clone())), .iter()
_ => None, .enumerate()
}) .find_map(|(i, entry)| match entry {
ContextEntry::ExistentialVar(n) if n == name.as_ref() => Some((i, None)),
ContextEntry::ExistentialSolved(n, t) if n == name.as_ref() => Some((i, Some(t.clone()))),
_ => None,
})
} }
#[inline] #[inline]

View file

@ -1,7 +1,7 @@
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use crate::{ use crate::{
bidir::synthesize, bidir::{app_ctx, synthesize},
data::{Context, Term}, data::{Context, Term},
}; };
@ -15,7 +15,9 @@ macro_rules! id_term {
fn test_id() -> Result<()> { fn test_id() -> Result<()> {
let id = id_term!(); let id = id_term!();
let ctx = Context::default(); let ctx = Context::default();
bail!("Result: {:?}", synthesize(&ctx, &id)); let (ty, out_ctx) = synthesize(&ctx, &id)?;
let ty2 = app_ctx(&out_ctx, &ty)?;
println!("Result: {:?}", ty2);
Ok(()) Ok(())
} }