use std::collections::VecDeque;
use std::str::FromStr;
use polytype::*;
#[test]
fn test_context_is_send() {
let context: Context<&'static str> = Context::default();
let _: &(dyn Send + Sync) = &context;
}
#[test]
fn test_tp_macro() {
assert_eq!(tp!(bool), Type::Constructed("bool", vec![]));
assert_eq!(
tp!(list(tp!(bool))),
Type::Constructed("list", vec![Type::Constructed("bool", vec![])]),
);
assert_eq!(
tp!(list(tp!(tuple(tp!(bool), tp!(int))))),
Type::Constructed(
"list",
vec![Type::Constructed(
"tuple",
vec![
Type::Constructed("bool", vec![]),
Type::Constructed("int", vec![]),
],
)],
),
);
assert_eq!(
tp!(list(
tp!(unusually_large_identifier_requiring_wrap),
tp!(unusually_large_identifier_requiring_wrap),
)),
Type::Constructed(
"list",
vec![
Type::Constructed("unusually_large_identifier_requiring_wrap", vec![]),
Type::Constructed("unusually_large_identifier_requiring_wrap", vec![]),
],
),
);
assert_eq!(tp!(0), Type::Variable(0));
assert_eq!(
tp!(hashmap(tp!(str), tp!(@arrow[tp!(int), tp!(0), tp!(bool)]))),
Type::Constructed(
"hashmap",
vec![
Type::Constructed("str", vec![]),
Type::arrow(
Type::Constructed("int", vec![]),
Type::arrow(Type::Variable(0), Type::Constructed("bool", vec![])),
),
],
)
);
assert_eq!(tp!(@arrow[Type::Variable(0)]), Type::Variable(0));
let arg = Type::Variable(0);
let ret = Type::Variable(1);
let t = tp!(@arrow[arg, ret]);
assert_eq!(t, tp!(@arrow[Type::Variable(0), Type::Variable(1)]));
assert_eq!(
tp!(@arrow[Type::Variable(0), Type::Variable(1), Type::Variable(2)]),
Type::arrow(
Type::Variable(0),
Type::arrow(Type::Variable(1), Type::Variable(2)),
)
);
assert_eq!(
tp!(@arrow[
Type::Variable(0),
Type::Variable(1),
Type::Variable(2),
Type::Variable(3),
]),
Type::arrow(
Type::Variable(0),
Type::arrow(
Type::Variable(1),
Type::arrow(Type::Variable(2), Type::Variable(3)),
),
)
);
}
#[test]
fn test_ptp_macro() {
assert_eq!(
ptp!(bool),
TypeScheme::Monotype(Type::Constructed("bool", vec![]))
);
assert_eq!(
ptp!(list(tp!(bool))),
TypeScheme::Monotype(Type::Constructed(
"list",
vec![Type::Constructed("bool", vec![])],
))
);
assert_eq!(
ptp!(0; 0),
TypeScheme::Polytype {
variable: 0,
body: Box::new(TypeScheme::Monotype(Type::Variable(0))),
}
);
assert_eq!(
ptp!(0; @arrow[tp!(0), tp!(0)]),
TypeScheme::Polytype {
variable: 0,
body: Box::new(TypeScheme::Monotype(Type::Constructed(
"→",
vec![Type::Variable(0), Type::Variable(0)],
))),
}
);
}
#[test]
fn test_arrow_methods() {
let t0 = Type::Variable(0);
let t1 = Type::Constructed("int", vec![]);
let t2 = Type::arrow(t0.clone(), t1.clone());
let ta1 = Type::arrow(t2.clone(), Type::arrow(t1.clone(), t0.clone()));
let ta2 = tp!(@arrow[t2.clone(), t1.clone(), t0.clone()]);
let ta3 = tp!(@arrow[tp!(@arrow[tp!(0), tp!(int)]), tp!(int), tp!(0)]);
assert_eq!(ta3, ta1);
assert_eq!(ta3, ta2);
let t = tp!(@arrow[tp!(@arrow[tp!(0), tp!(int)]), tp!(int), tp!(0)]);
assert_eq!(
t.args(),
Some(VecDeque::from(vec![
&tp!(@arrow[tp!(0), tp!(int)]),
&tp!(int),
])),
);
assert_eq!(t.returns(), Some(&tp!(0)));
}
#[test]
fn test_tp_from_vecdeque() {
let mut tps = VecDeque::new();
tps.push_back(Type::Variable(0));
let tp: Type = tps.clone().into();
assert_eq!(tp, Type::Variable(0));
tps.push_back(Type::Variable(1));
let tp: Type = tps.clone().into();
assert_eq!(tp, Type::arrow(Type::Variable(0), Type::Variable(1)));
tps.push_back(Type::Variable(2));
let tp: Type = tps.clone().into();
assert_eq!(
tp,
Type::arrow(
Type::Variable(0),
Type::arrow(Type::Variable(1), Type::Variable(2))
)
);
tps.push_back(Type::Variable(3));
let tp: Type = tps.clone().into();
assert_eq!(
tp,
Type::arrow(
Type::Variable(0),
Type::arrow(
Type::Variable(1),
Type::arrow(Type::Variable(2), Type::Variable(3))
)
)
);
}
#[test]
fn test_tp_from_vec() {
let mut tps = Vec::new();
tps.push(Type::Variable(0));
let tp: Type = tps.clone().into();
assert_eq!(tp, Type::Variable(0));
tps.push(Type::Variable(1));
let tp: Type = tps.clone().into();
assert_eq!(tp, Type::arrow(Type::Variable(0), Type::Variable(1)));
tps.push(Type::Variable(2));
let tp: Type = tps.clone().into();
assert_eq!(
tp,
Type::arrow(
Type::Variable(0),
Type::arrow(Type::Variable(1), Type::Variable(2))
)
);
tps.push(Type::Variable(3));
let tp: Type = tps.clone().into();
assert_eq!(
tp,
Type::arrow(
Type::Variable(0),
Type::arrow(
Type::Variable(1),
Type::arrow(Type::Variable(2), Type::Variable(3))
)
)
);
}
#[test]
fn test_unify_one_side_polymorphic() {
let mut ctx = Context::default();
ctx.unify(
&tp!(list(tp!(@arrow[tp!(int), tp!(bool)]))),
&tp!(list(tp!(0))),
)
.expect("one side polymorphic");
}
#[test]
fn test_unify_one_side_polymorphic_fail() {
let mut ctx = Context::default();
ctx.unify(&tp!(@arrow[tp!(int), tp!(bool)]), &tp!(list(tp!(0))))
.expect_err("incompatible types");
}
#[test]
fn test_unify_both_sides_polymorphic() {
let mut ctx = Context::default();
ctx.unify(
&tp!(list(tp!(@arrow[tp!(int), tp!(0)]))),
&tp!(list(tp!(@arrow[tp!(1), tp!(bool)]))),
)
.expect("both sides polymorphic");
}
#[test]
fn test_unify_both_sides_polymorphic_occurs() {
let mut ctx = Context::default();
ctx.unify(&tp!(0), &tp!(list(tp!(@arrow[tp!(0), tp!(bool)]))))
.expect_err("circular polymorphic types");
}
#[test]
fn test_unify_nonstring_name() {
#[derive(Debug, Clone, PartialEq, Eq)]
struct N(u32);
impl Name for N {
fn arrow() -> Self {
N(0)
}
}
let ts = TypeScheme::Polytype {
variable: 0,
body: Box::new(TypeScheme::Monotype(Type::Constructed(
N(3),
vec![Type::Variable(0)],
))),
};
let mut ctx = Context::default();
let t = ts.instantiate(&mut ctx);
ctx.unify(
&Type::Constructed(
N(3),
vec![Type::arrow(
Type::Constructed(N(1), vec![]),
Type::Constructed(N(2), vec![]),
)],
),
&t,
)
.expect("nonstring one side polymorphic");
let mut ctx = Context::default();
let t = ts.instantiate(&mut ctx);
ctx.unify(
&Type::arrow(
Type::Constructed(N(1), vec![]),
Type::Constructed(N(2), vec![]),
),
&t,
)
.expect_err("nonstring incompatible types");
}
#[test]
fn test_merge_no_sacreds() {
let mut ctx = Context::default();
let a = ctx.new_variable();
let b = ctx.new_variable();
let _ = ctx.new_variable();
ctx.unify(&Type::arrow(a, b), &tp!(@arrow[tp!(int), tp!(bool)]))
.unwrap();
let mut ctx2 = Context::default();
let _ = ctx2.new_variable();
let pt = ptp!(0, 1; @arrow[tp!(0), tp!(1)]);
let mut t = pt.instantiate(&mut ctx2);
ctx2.extend(1, tp!(bool));
let mut last = ctx2.new_variable();
assert_eq!(t.apply(&ctx2).to_string(), "bool → t2");
let ctx_change = ctx.merge(ctx2, vec![]);
ctx_change.reify_type(&mut t);
assert_eq!(t.to_string(), "t4 → t5");
assert_eq!(t.apply(&ctx).to_string(), "bool → t5");
ctx_change.reify_type(&mut last);
assert_eq!(last, tp!(6));
assert_eq!(ctx.new_variable(), tp!(7));
}
#[test]
fn test_merge_with_sacreds() {
let mut ctx = Context::default();
let a = ctx.new_variable();
let b = ctx.new_variable();
let _ = ctx.new_variable();
ctx.unify(&Type::arrow(a, b), &tp!(@arrow[tp!(int), tp!(bool)]))
.unwrap();
let mut ctx2 = Context::default();
let _ = ctx2.new_variable();
let pt = ptp!(0, 1; @arrow[tp!(0), tp!(1)]);
let mut t = pt.instantiate(&mut ctx2);
ctx2.extend(2, tp!(bool));
let mut last = ctx2.new_variable();
assert_eq!(t.apply(&ctx2).to_string(), "t1 → bool");
let ctx_change = ctx.merge(ctx2, vec![0, 1]);
ctx_change.reify_type(&mut t);
assert_eq!(t.to_string(), "t1 → t5");
assert_eq!(t.apply(&ctx).to_string(), "bool → bool");
ctx_change.reify_type(&mut last);
assert_eq!(last, tp!(6));
assert_eq!(ctx.new_variable(), tp!(7));
}
#[cfg(feature = "parser")]
#[test]
fn test_parse() {
let t = tp!(int);
assert_eq!(&t, &Type::from_str("int").expect("parse 1"));
assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 2"));
let t = tp!(0);
assert_eq!(&t, &Type::from_str("t0").expect("parse 3"));
assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 4"));
let t = tp!(@arrow[tp!(int), tp!(int)]);
assert_eq!(&t, &Type::from_str("int -> int").expect("parse 5"));
assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 6"));
let t = tp!(list(tp!(@arrow[tp!(int), tp!(2)])));
assert_eq!(&t, &Type::from_str("list(int -> t2)").expect("parse 7"));
assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 8"));
let t = tp!(hashmap(tp!(str), tp!(@arrow[tp!(int), tp!(0), tp!(bool)])));
assert_eq!(
&t,
&Type::from_str("hashmap(str, int -> t0 -> bool)").expect("parse 9")
);
assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 10"));
let t = tp!(@arrow[
tp!(@arrow[tp!(1), tp!(0), tp!(1)]),
tp!(1),
tp!(list(tp!(0))),
tp!(1),
]);
assert_eq!(
&t,
&Type::from_str("(t1 → t0 → t1) → t1 → list(t0) → t1").expect("parse 11")
);
assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 12"));
}