use itertools::Itertools;
use std::collections::{HashMap, VecDeque};
use std::fmt;
use crate::{Context, Name};
pub type Variable = usize;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum TypeScheme<N: Name = &'static str> {
Monotype(Type<N>),
Polytype {
variable: Variable,
body: Box<TypeScheme<N>>,
},
}
impl<N: Name> TypeScheme<N> {
pub fn is_bound(&self, v: Variable) -> bool {
match *self {
TypeScheme::Monotype(_) => false,
TypeScheme::Polytype { variable, .. } if variable == v => true,
TypeScheme::Polytype { ref body, .. } => body.is_bound(v),
}
}
pub fn bound_vars(&self) -> Vec<Variable> {
let mut t = self;
let mut bvs = Vec::new();
while let TypeScheme::Polytype { variable, ref body } = *t {
bvs.push(variable);
t = body
}
bvs
}
pub fn free_vars(&self) -> Vec<Variable> {
let mut vars = vec![];
self.free_vars_internal(&mut vars);
vars.sort_unstable();
vars.dedup();
vars
}
fn free_vars_internal(&self, vars: &mut Vec<Variable>) {
match *self {
TypeScheme::Monotype(ref t) => t.vars_internal(vars),
TypeScheme::Polytype { variable, ref body } => {
body.free_vars_internal(vars);
*vars = vars.iter().filter(|&v| v != &variable).cloned().collect();
}
}
}
pub fn instantiate(&self, ctx: &mut Context<N>) -> Type<N> {
self.instantiate_internal(ctx, &mut HashMap::new())
}
fn instantiate_internal(
&self,
ctx: &mut Context<N>,
substitution: &mut HashMap<Variable, Type<N>>,
) -> Type<N> {
match *self {
TypeScheme::Monotype(ref t) => t.substitute(substitution),
TypeScheme::Polytype { variable, ref body } => {
substitution.insert(variable, ctx.new_variable());
body.instantiate_internal(ctx, substitution)
}
}
}
pub fn instantiate_owned(self, ctx: &mut Context<N>) -> Type<N> {
self.instantiate_owned_internal(ctx, &mut HashMap::new())
}
fn instantiate_owned_internal(
self,
ctx: &mut Context<N>,
substitution: &mut HashMap<Variable, Type<N>>,
) -> Type<N> {
match self {
TypeScheme::Monotype(mut t) => {
t.substitute_mut(substitution);
t
}
TypeScheme::Polytype { variable, body } => {
substitution.insert(variable, ctx.new_variable());
body.instantiate_owned_internal(ctx, substitution)
}
}
}
}
impl<N: Name> fmt::Display for TypeScheme<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self {
TypeScheme::Polytype { variable, ref body } => write!(f, "∀t{}. {}", variable, body),
TypeScheme::Monotype(ref t) => t.fmt(f),
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Type<N: Name = &'static str> {
Constructed(N, Vec<Type<N>>),
Variable(Variable),
}
impl<N: Name> Type<N> {
pub fn arrow(alpha: Type<N>, beta: Type<N>) -> Type<N> {
Type::Constructed(N::arrow(), vec![alpha, beta])
}
pub fn as_arrow(&self) -> Option<(&Type<N>, &Type<N>)> {
match *self {
Type::Constructed(ref n, ref args) if n.is_arrow() => Some((&args[0], &args[1])),
_ => None,
}
}
pub(crate) fn occurs(&self, v: Variable) -> bool {
match *self {
Type::Constructed(_, ref args) => args.iter().any(|t| t.occurs(v)),
Type::Variable(n) => n == v,
}
}
pub(crate) fn show(&self, is_return: bool) -> String {
match *self {
Type::Variable(v) => format!("t{}", v),
Type::Constructed(ref name, ref args) => {
if args.is_empty() {
name.show()
} else if name.is_arrow() {
Type::arrow_show(args, is_return)
} else {
format!(
"{}({})",
name.show(),
args.iter().map(|t| t.show(true)).join(",")
)
}
}
}
}
fn arrow_show(args: &[Type<N>], is_return: bool) -> String {
if is_return {
format!("{} → {}", args[0].show(false), args[1].show(true))
} else {
format!("({} → {})", args[0].show(false), args[1].show(true))
}
}
pub fn args(&self) -> Option<VecDeque<&Type<N>>> {
match *self {
Type::Constructed(ref n, ref args) if n.is_arrow() => {
let mut tps = VecDeque::with_capacity(1);
tps.push_back(&args[0]);
let mut tp = &args[1];
loop {
match *tp {
Type::Constructed(ref n, ref args) if n.is_arrow() => {
tps.push_back(&args[0]);
tp = &args[1];
}
_ => break,
}
}
Some(tps)
}
_ => None,
}
}
pub fn args_destruct(self) -> Option<Vec<Type<N>>> {
match self {
Type::Constructed(n, mut args) if n.is_arrow() => {
let mut tps = Vec::with_capacity(1);
let mut tp = args.pop().unwrap();
tps.push(args.pop().unwrap());
loop {
match tp {
Type::Constructed(n, mut args) if n.is_arrow() => {
tp = args.pop().unwrap();
tps.push(args.pop().unwrap());
}
_ => break,
}
}
Some(tps)
}
_ => None,
}
}
pub fn returns(&self) -> Option<&Type<N>> {
match *self {
Type::Constructed(ref n, ref args) if n.is_arrow() => {
let mut tp = &args[1];
loop {
match *tp {
Type::Constructed(ref n, ref args) if n.is_arrow() => {
tp = &args[1];
}
_ => break,
}
}
Some(tp)
}
_ => None,
}
}
pub fn apply(&self, ctx: &Context<N>) -> Type<N> {
match *self {
Type::Constructed(ref name, ref args) => {
let args = args.iter().map(|t| t.apply(ctx)).collect();
Type::Constructed(name.clone(), args)
}
Type::Variable(v) => {
let maybe_tp = ctx
.path_compression_cache
.read()
.get(&v)
.or_else(|| ctx.substitution.get(&v))
.cloned();
maybe_tp
.map(|mut tp| {
tp.apply_mut(ctx);
let mut cache = ctx.path_compression_cache.write();
let is_hit = cache.get(&v) == Some(&tp);
if !is_hit {
cache.insert(v, tp.clone());
}
tp
})
.unwrap_or_else(|| self.clone())
}
}
}
pub fn apply_mut(&mut self, ctx: &Context<N>) {
match *self {
Type::Constructed(_, ref mut args) => {
for t in args {
t.apply_mut(ctx)
}
}
Type::Variable(v) => {
let maybe_tp = ctx
.path_compression_cache
.read()
.get(&v)
.or_else(|| ctx.substitution.get(&v))
.cloned();
*self = maybe_tp
.map(|mut tp| {
tp.apply_mut(ctx);
ctx.path_compression_cache.write().insert(v, tp.clone());
tp
})
.unwrap_or_else(|| self.clone());
}
}
}
pub fn generalize(&self, bound: &[Variable]) -> TypeScheme<N> {
let fvs = self
.vars()
.into_iter()
.filter(|x| !bound.contains(x))
.collect::<Vec<Variable>>();
let mut t = TypeScheme::Monotype(self.clone());
for v in fvs {
t = TypeScheme::Polytype {
variable: v,
body: Box::new(t),
};
}
t
}
pub fn vars(&self) -> Vec<Variable> {
let mut vars = vec![];
self.vars_internal(&mut vars);
vars.sort_unstable();
vars.dedup();
vars
}
fn vars_internal(&self, vars: &mut Vec<Variable>) {
match *self {
Type::Constructed(_, ref args) => {
for arg in args {
arg.vars_internal(vars);
}
}
Type::Variable(v) => vars.push(v),
}
}
pub fn substitute(&self, substitution: &HashMap<Variable, Type<N>>) -> Type<N> {
match *self {
Type::Constructed(ref name, ref args) => {
let args = args.iter().map(|t| t.substitute(substitution)).collect();
Type::Constructed(name.clone(), args)
}
Type::Variable(v) => substitution.get(&v).cloned().unwrap_or(Type::Variable(v)),
}
}
pub fn substitute_mut(&mut self, substitution: &HashMap<Variable, Type<N>>) {
match *self {
Type::Constructed(_, ref mut args) => {
for t in args {
t.substitute_mut(substitution)
}
}
Type::Variable(v) => {
if let Some(t) = substitution.get(&v) {
*self = t.clone()
}
}
}
}
}
impl<N: Name> fmt::Display for Type<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", self.show(true))
}
}
impl<N: Name> From<VecDeque<Type<N>>> for Type<N> {
fn from(mut tps: VecDeque<Type<N>>) -> Type<N> {
match tps.len() {
0 => panic!("cannot create a type from nothing"),
1 => tps.pop_front().unwrap(),
2 => {
let alpha = tps.pop_front().unwrap();
let beta = tps.pop_front().unwrap();
Type::arrow(alpha, beta)
}
_ => {
let alpha = tps.pop_front().unwrap();
Type::arrow(alpha, tps.into())
}
}
}
}
impl<N: Name> From<Vec<Type<N>>> for Type<N> {
fn from(mut tps: Vec<Type<N>>) -> Type<N> {
let mut beta = tps
.pop()
.unwrap_or_else(|| panic!("cannot create a type from nothing"));
while let Some(alpha) = tps.pop() {
beta = Type::arrow(alpha, beta)
}
beta
}
}