convert
This commit is contained in:
parent
6491a7ded9
commit
327a4e0400
8 changed files with 431 additions and 4 deletions
9
bidir/src/abstract_data.rs
Normal file
9
bidir/src/abstract_data.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
pub trait Data {
|
||||
/* Required methods */
|
||||
/* */
|
||||
|
||||
/* Provided methods */
|
||||
/* */
|
||||
|
||||
fn synthesize() {}
|
||||
}
|
0
bidir/src/bidir_debruijn.rs
Normal file
0
bidir/src/bidir_debruijn.rs
Normal file
51
bidir/src/convert_data.rs
Normal file
51
bidir/src/convert_data.rs
Normal 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)
|
||||
}
|
|
@ -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
332
bidir/src/data_debruijn.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
|
|
28
hwk2.typ
28
hwk2.typ
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue