This commit is contained in:
Michael Zhang 2023-11-13 02:52:32 -06:00
parent 6491a7ded9
commit 327a4e0400
8 changed files with 431 additions and 4 deletions

View File

@ -0,0 +1,9 @@
pub trait Data {
/* Required methods */
/* */
/* Provided methods */
/* */
fn synthesize() {}
}

View File

51
bidir/src/convert_data.rs Normal file
View File

@ -0,0 +1,51 @@
use crate::DEPTH;
use crate::{data, data_debruijn, gensym::gensym_type};
pub fn convert_term(term: &data::Term) -> data_debruijn::Term {
fn convert_term_with_context(ctx: &data::Context, term: &data::Term) -> data_debruijn::Term {
match term {
data::Term::Unit => data_debruijn::Term::Unit,
data::Term::Var(name) => {
let (idx, _) = ctx.lookup_type(name).unwrap();
let ctx_len = ctx.0.len();
data_debruijn::Term::Var(ctx_len - 1 - idx)
}
data::Term::Lam(arg, body) => {
let ty = gensym_type();
let ctx2 = ctx.add(vec![data::ContextEntry::TermAnnot(
arg.to_owned(),
data::Type::Var(ty.clone()),
)]);
let body = convert_term_with_context(&ctx2, body);
data_debruijn::Term::Lam(Box::new(body))
}
data::Term::App(func, arg) => {
let func = convert_term_with_context(ctx, &func);
let arg = convert_term_with_context(ctx, &arg);
data_debruijn::Term::App(Box::new(func), Box::new(arg))
}
data::Term::Annot(term, ty) => {
let term = convert_term_with_context(ctx, &term);
let ty = convert_ty_with_context(ctx, &ty);
data_debruijn::Term::Annot(Box::new(term), ty)
}
}
}
fn convert_ty_with_context(ctx: &data::Context, ty: &data::Type) -> data_debruijn::Type {
match ty {
data::Type::Unit => data_debruijn::Type::Unit,
data::Type::Var(_) => todo!(),
data::Type::Existential(_) => todo!(),
data::Type::Polytype(_, _) => todo!(),
data::Type::Arrow(_, _) => todo!(),
}
}
convert_term_with_context(&data::Context::default(), term)
}

View File

@ -1,4 +1,4 @@
use std::{fmt, hash::Hash, mem::replace};
use std::{fmt, hash::Hash};
use anyhow::{bail, Result};
use im::{HashSet, Vector};

332
bidir/src/data_debruijn.rs Normal file
View File

@ -0,0 +1,332 @@
use std::{fmt, hash::Hash};
use anyhow::{bail, Result};
use im::{HashSet, Vector};
use crate::gensym::gensym_existential;
/// A lambda calculus term.
#[derive(Clone)]
pub enum Term {
/// Unit type
Unit,
/// Variable, with a reference into the context.
/// The entry pointed to by this index MUST be a TypeVar.
Var(usize),
/// Lambda abstraction.
Lam(Box<Term>),
/// Lambda application.
App(Box<Term>, Box<Term>),
/// Type annotation
Annot(Box<Term>, Type),
}
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(arg1) => write!(f, "(λ.{:?})", arg1),
Self::App(arg0, arg1) => write!(f, "({:?} · {:?})", arg0, arg1),
Self::Annot(arg0, arg1) => write!(f, "({:?} : {:?})", arg0, arg1),
}
}
}
#[derive(Clone)]
pub enum Type {
Unit,
Var(usize),
Existential(usize),
Polytype(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, "{}", arg0),
Self::Polytype(arg1) => write!(f, "(∀.{:?})", arg1),
Self::Arrow(arg0, arg1) => write!(f, "({:?} -> {:?})", arg0, arg1),
}
}
}
impl Type {
/// Attempt to turn this polytype into a monotype.
///
/// This fails if any sub-expression of this type contains a polytype expression.
pub fn try_into_mono(&self) -> Option<Monotype> {
match self {
Type::Unit => Some(Monotype::Unit),
Type::Var(x) => Some(Monotype::Var(*x)),
Type::Existential(x) => Some(Monotype::Existential(*x)),
// Polytypes cannot be converted to monotypes
Type::Polytype(_) => None,
Type::Arrow(a, b) => Some(Monotype::Arrow(
Box::new(a.try_into_mono()?),
Box::new(b.try_into_mono()?),
)),
}
}
#[inline]
pub fn is_mono(&self) -> bool {
self.try_into_mono().is_some()
}
}
impl Type {
// pub fn free_vars(&self) -> HashSet<FreeVar> {
// fn free_vars_with_context(ctx: &Context, ty: &Type) -> HashSet<FreeVar> {
// match ty {
// Type::Unit => HashSet::default(),
// Type::Var(x) if ctx.lookup_type(x).is_some() => HashSet::default(),
// Type::Var(x) => [FreeVar::Var(x.to_owned())].into_iter().collect(),
// Type::Existential(x) if ctx.lookup_existential(x).is_some() => HashSet::default(),
// Type::Existential(x) => [FreeVar::Existential(x.to_owned())].into_iter().collect(),
// Type::Polytype(x, ty_a) => {
// let new_ctx = ctx.add(vec![ContextEntry::ExistentialVar(x.to_owned())]);
// free_vars_with_context(&new_ctx, &ty_a)
// }
// Type::Arrow(ty_a, ty_b) => {
// let a_vars = free_vars_with_context(&ctx, &ty_a);
// let b_vars = free_vars_with_context(&ctx, &ty_b);
// a_vars.union(b_vars)
// }
// }
// }
// free_vars_with_context(&Context::default(), self)
// }
// /// Substitute all variables with this variable with the replacement type.
// pub fn subst(&self, var: &str, replacement: &Type) -> Type {
// match self {
// Type::Unit => Type::Unit,
// Type::Var(n) if n == var => replacement.clone(),
// Type::Var(n) => Type::Var(n.clone()),
// Type::Existential(s) if s == var => replacement.clone(),
// Type::Existential(s) => Type::Existential(s.clone()),
// Type::Polytype(t) => {
// // Type::Polytype(a.clone(), Box::new(t.subst(var, replacement))),
// todo!()
// }
// Type::Arrow(a, b) => Type::Arrow(
// Box::new(a.subst(var, replacement)),
// Box::new(b.subst(var, replacement)),
// ),
// }
// }
}
#[derive(Clone)]
pub enum Monotype {
Unit,
Var(usize),
Existential(usize),
Arrow(Box<Monotype>, Box<Monotype>),
}
impl fmt::Debug for Monotype {
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, "{}", arg0),
Self::Arrow(arg0, arg1) => write!(f, "({arg0:?} -> {arg1:?})"),
}
}
}
impl Monotype {
pub fn into_poly(&self) -> Type {
match self {
Monotype::Unit => Type::Unit,
Monotype::Var(x) => Type::Var(x.clone()),
Monotype::Existential(x) => Type::Existential(x.clone()),
Monotype::Arrow(a, b) => Type::Arrow(Box::new(a.into_poly()), Box::new(b.into_poly())),
}
}
}
#[derive(Clone)]
pub enum ContextEntry {
TypeVar(String),
TermAnnot(String, Type),
ExistentialVar(String),
ExistentialSolved(String, Monotype),
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)]
pub enum CompleteContextEntry {
TypeVar(String),
TermAnnot(String, Type),
ExistentialSolved(String, Monotype),
Marker(String),
}
#[derive(Clone, Default)]
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 {
pub fn add(&self, entries: Vec<ContextEntry>) -> Context {
let mut new_ctx = self.clone();
for entry in entries {
new_ctx.0.push_back(entry);
}
new_ctx
}
/// Looks up a polytype by name, also returning an index
pub fn lookup_type(&self, name: impl AsRef<str>) -> Option<(usize, Type)> {
self
.0
.iter()
.enumerate()
.find_map(|(i, entry)| match entry {
ContextEntry::TermAnnot(n, t) if n == name.as_ref() => Some((i, t.clone())),
_ => None,
})
}
#[inline]
pub fn has_type(&self, name: impl AsRef<str>) -> bool {
self.lookup_type(name).is_some()
}
/// Looks up an existential variable by name
/// - Returns Some(Some(t)) if solved
/// - Returns Some(None) if exists but unsolved
/// - Returns None if not found
pub fn lookup_existential(&self, name: impl AsRef<str>) -> Option<(usize, Option<Monotype>)> {
self
.0
.iter()
.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]
pub fn has_existential(&self, name: impl AsRef<str>) -> bool {
self.lookup_existential(name).is_some()
}
/// Returns a list of names of unsolved existentials
pub fn unsolved_existentials(&self) -> HashSet<String> {
let mut unsolved = HashSet::new();
for entry in self.0.iter() {
match entry {
ContextEntry::ExistentialVar(n) => {
unsolved.insert(n.clone());
}
_ => {}
}
}
unsolved
}
/// Returns (before, after)
pub fn split_by<P>(&self, p: P) -> Option<(Context, Context)>
where
P: Fn(&ContextEntry) -> bool,
{
let (before, after) = {
let idx = self.0.iter().position(p)?;
self.0.clone().split_at(idx)
};
Some((Context(before), Context(after)))
}
pub fn unsplit(left: &Context, center: ContextEntry, right: &Context) -> Context {
let mut res = left.clone();
res.0.push_back(center);
res.0.extend(right.0.clone().into_iter());
res
}
// pub fn split_existential_function(&self, var: &str) -> Result<(String, String, Context)> {
// let mut ctx = self.clone();
// let idx = match ctx.lookup_existential(var) {
// Some((idx, _)) => idx,
// None => bail!("wtf?"),
// };
// let ex_a1_s = gensym_existential();
// let ex_a2_s = gensym_existential();
// ctx.0[idx] = ContextEntry::ExistentialSolved(
// var.to_owned(),
// Monotype::Arrow(
// Box::new(Monotype::Existential(ex_a1_s.to_owned())),
// Box::new(Monotype::Existential(ex_a2_s.to_owned())),
// ),
// );
// ctx
// .0
// .insert(idx, ContextEntry::ExistentialVar(ex_a1_s.to_owned()));
// ctx
// .0
// .insert(idx, ContextEntry::ExistentialVar(ex_a2_s.to_owned()));
// Ok((ex_a1_s, ex_a2_s, ctx))
// }
}
/// An index into the context.
pub struct ContextIndex {
context_length: usize,
context_index: usize,
context_level: usize,
}
impl ContextIndex {
/// Bump an index, creating a new one when entering a new context level
///
/// This should ensure the index is still referring to the same context entry.
pub fn bump(&self) -> Self {
ContextIndex {
context_length: self.context_length + 1,
context_index: self.context_index + 1,
context_level: self.context_level,
}
}
}

View File

@ -5,9 +5,15 @@ extern crate trace;
use lalrpop_util::lalrpop_mod;
pub mod abstract_data;
pub mod bidir;
pub mod data;
pub mod convert_data;
pub mod bidir_debruijn;
pub mod data_debruijn;
mod gensym;
#[cfg(test)]
mod tests;

View File

@ -1,6 +1,7 @@
use anyhow::Result;
use bidir::{
bidir::{app_ctx, synthesize},
convert_data::convert_term,
data::Context,
parser::TermParser,
};
@ -18,6 +19,8 @@ fn main() -> Result<()> {
Err(_) => break,
};
// let line = r"\x.\y.x";
let parsed_term = match term_parser.parse(&line) {
Ok(term) => term,
Err(err) => {
@ -28,6 +31,9 @@ fn main() -> Result<()> {
println!("parsed: {:?}", parsed_term);
let ctx = Context::default();
let converted_term = convert_term(&parsed_term);
println!("converted: {converted_term:?}");
let (ty, out_ctx) = match synthesize(&ctx, &parsed_term) {
Ok(v) => v,
Err(err) => {
@ -47,6 +53,7 @@ fn main() -> Result<()> {
};
println!("synthesized: {:?}", ty);
// break;
}
Ok(())

View File

@ -2,6 +2,12 @@
#set page("us-letter")
#import "@preview/prooftrees:0.1.0": *
#import emoji: face
#let prooftree(..args) = tree(
tree_config: tree_config(
vertical_spacing: 6pt,
),
..args
)
= Homework 2
@ -11,10 +17,12 @@ Michael Zhang \<zhan4854\@umn.edu\>
set text(gray)
body
}
#let subst = $"subst"$
#let tt = $"tt"$
#c[Assume you have $"Id"$ and $"U"$ but not $"Eq"$ (or $hat("Eq")$). Write down an abstraction $p$ such that
#tree(
#prooftree(
axi[$a in A [Gamma]$],
axi[$b in B [Gamma]$],
bin[$p(a, b) in "Id"(A+B, "inl"(a), "inr"(b)) arrow.r emptyset [Gamma]$]
@ -25,11 +33,25 @@ is derivable. You do not have to prove in your submission that it is derivable.]
Wait isn't this just the same as the Peano's fourth axiom as given in the book?
$
p(a, b) &equiv lambda ((x) "subst"(x, "tt")) \
p(a, b) &equiv lambda ((x) subst(x, tt)) \
$
where
- $P(m) = "Set"("when"(m, (x)hat(top), (y)hat({})))$
- $subst(c, p) equiv "apply"("idpeel"(c, (x) lambda x.x), p)$
#prooftree(
axi[$A "set"$],
axi[$B "set"$],
bin[$A + B "set"$],
axi[$a in A$],
axi[$b in B$],
axi[$p in "Id"(A + B, "inl"(a), "inr"(b))$],
axi[$P(x) "set" [x in A + B]$],
nary(5)[$subst(p, "inl"(a)) equiv "apply"("idpeel"(p, (g) lambda y.y), "inl"(a)) in P("inr"(b))$],
)
- $P(m) equiv "Set"("when"(m, (x)hat(top), (y)hat({})))$
// (Agda implementation #face.smile.slight)