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 anyhow::{bail, Result};
use im::{HashSet, Vector}; 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; use lalrpop_util::lalrpop_mod;
pub mod abstract_data;
pub mod bidir; pub mod bidir;
pub mod data; pub mod data;
pub mod convert_data;
pub mod bidir_debruijn;
pub mod data_debruijn;
mod gensym; mod gensym;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View file

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

View file

@ -2,6 +2,12 @@
#set page("us-letter") #set page("us-letter")
#import "@preview/prooftrees:0.1.0": * #import "@preview/prooftrees:0.1.0": *
#import emoji: face #import emoji: face
#let prooftree(..args) = tree(
tree_config: tree_config(
vertical_spacing: 6pt,
),
..args
)
= Homework 2 = Homework 2
@ -11,10 +17,12 @@ Michael Zhang \<zhan4854\@umn.edu\>
set text(gray) set text(gray)
body 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 #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[$a in A [Gamma]$],
axi[$b in B [Gamma]$], axi[$b in B [Gamma]$],
bin[$p(a, b) in "Id"(A+B, "inl"(a), "inr"(b)) arrow.r emptyset [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? 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 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) // (Agda implementation #face.smile.slight)