gensym
This commit is contained in:
parent
f88f349934
commit
2863e10113
3 changed files with 104 additions and 45 deletions
|
@ -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())),
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue