use crate::r#type::{Type, Type::*, util::*}; impl Type { /// Checks if this type is known as the given other type, /// ie. checks if there exists an implicit conversion from /// one to the other. /// /// Currently this is only needed for list to vec conversions. /// ```rust /// use myslip::r#type::{Type::*, util::*}; /// assert!(List(vec![Integer, Integer, Integer]).aka(&vecof(Integer))); /// ``` /// /// Preferrably this should also work with generics: /// ```rust /// use myslip::r#type::{Type::*, util::*}; /// assert!(List(vec![Integer, Boolean, Integer]).aka(&vecof(vt("T")))); /// assert!(List(vec![Integer, Boolean, vt("X")]).aka(&vecof(vt("T")))); /// ``` /// /// But of course it also should know when to fail: /// ```rust /// use myslip::r#type::{Type::*, util::*}; /// assert!(!List(vec![Integer, Boolean]).aka(&vecof(Integer))); /// assert!(!List(vec![Integer, Boolean]).aka(&vecof(Boolean))); /// ``` pub fn aka(&self, known_as: &Type) -> bool { self.least_general_supertype(known_as) == *known_as } fn least_general_supertype(&self, other: &Type) -> Type { match (self, other) { (a, b) if a == b => a.clone(), (VecOf(a), VecOf(b)) => vecof(a.least_general_supertype(b)), (List(v1), List(v2)) if v1.len() == v2.len() => { let mut res = vec![]; for (a, b) in v1.into_iter().zip(v2) { res.push(a.least_general_supertype(b)); } List(res) }, (List(v1), List(v2)) => match v1.get(0).or(v2.get(0)) { Some(first) => vecof( v1.into_iter() .chain(v2) .fold(first.clone(), |a, b| a.least_general_supertype(b)) ), None => panic!("unreachable - types would be equal") }, (List(v), VecOf(b)) => vecof( v.into_iter() .fold(*b.clone(), |a, b| a.least_general_supertype(b)) ), (VecOf(b), List(v)) => vecof( v.into_iter() .fold(*b.clone(), |a, b| a.least_general_supertype(b)) ), _ => vt("T") } } } #[cfg(test)] mod tests { use crate::r#type::{Type::*, util::*}; #[test] fn test_basic_general_supertypes() { assert_eq!( Integer.least_general_supertype(&Integer), Integer, "if two types are equal, they get the same supertype" ); assert_eq!( Integer.least_general_supertype(&Boolean), vt("T"), "if two types have nothing in common, vt(\"t\") is returned" ); } #[test] fn test_compound_general_supertypes() { assert_eq!( List(vec![Integer, Integer]) .least_general_supertype( &List(vec![Integer, Integer]) ), List(vec![Integer, Integer]) ); assert_eq!( List(vec![Integer, Boolean]) .least_general_supertype( &List(vec![Integer, Integer]) ), List(vec![Integer, vt("T")]) ); assert_eq!( vecof(Integer) .least_general_supertype( &vecof(Integer) ), vecof(Integer) ); assert_eq!( vecof(Integer) .least_general_supertype( &vecof(Boolean) ), vecof(vt("T")) ); assert_eq!( List(vec![Integer, Boolean]).least_general_supertype( &List(vec![Boolean, Integer]) ), List(vec![vt("T"), vt("T")]) ); } #[test] fn test_conversion_in_general_supertypes() { assert_eq!( List(vec![Integer, Integer]).least_general_supertype( &vecof(Integer) ), vecof(Integer) ); assert_eq!( List(vec![Integer, Integer]).least_general_supertype( &List(vec![Integer, Integer, Integer]) ), vecof(Integer) ); assert_eq!( List(vec![Integer, Boolean]).least_general_supertype( &List(vec![Integer, Boolean, Integer]) ), vecof(vt("T")) ); assert_eq!( List(vec![Boolean, Integer]).least_general_supertype( &vecof(Integer) ), vecof(vt("T")) ); } }