use crate::{Kind, Ty, TyCon, TyList, TyVar};
use bhc_intern::Symbol;
#[must_use]
pub fn dyn_tensor_tycon() -> TyCon {
TyCon::new(Symbol::intern("DynTensor"), Kind::star_to_star())
}
#[must_use]
pub fn dyn_tensor_of(elem_ty: Ty) -> Ty {
Ty::App(Box::new(Ty::Con(dyn_tensor_tycon())), Box::new(elem_ty))
}
#[must_use]
pub fn shape_witness_tycon() -> TyCon {
let kind = Kind::Arrow(Box::new(Kind::nat_list()), Box::new(Kind::Star));
TyCon::new(Symbol::intern("ShapeWitness"), kind)
}
#[must_use]
pub fn shape_witness_of(shape: TyList) -> Ty {
Ty::App(
Box::new(Ty::Con(shape_witness_tycon())),
Box::new(Ty::TyList(shape)),
)
}
#[must_use]
pub fn to_dynamic_type(tensor_tycon: &TyCon) -> Ty {
let shape_var = TyVar::new(0, Kind::nat_list());
let a_var = TyVar::new(1, Kind::Star);
let tensor_type = Ty::App(
Box::new(Ty::App(
Box::new(Ty::Con(tensor_tycon.clone())),
Box::new(Ty::TyList(TyList::Var(shape_var.clone()))),
)),
Box::new(Ty::Var(a_var.clone())),
);
let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
let fun_type = Ty::fun(tensor_type, dyn_tensor_type);
Ty::Forall(vec![shape_var, a_var], Box::new(fun_type))
}
#[must_use]
pub fn from_dynamic_type(tensor_tycon: &TyCon, maybe_tycon: &TyCon) -> Ty {
let shape_var = TyVar::new(0, Kind::nat_list());
let a_var = TyVar::new(1, Kind::Star);
let witness_type = shape_witness_of(TyList::Var(shape_var.clone()));
let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
let tensor_type = Ty::App(
Box::new(Ty::App(
Box::new(Ty::Con(tensor_tycon.clone())),
Box::new(Ty::TyList(TyList::Var(shape_var.clone()))),
)),
Box::new(Ty::Var(a_var.clone())),
);
let maybe_tensor = Ty::App(
Box::new(Ty::Con(maybe_tycon.clone())),
Box::new(tensor_type),
);
let fun_type = Ty::fun(witness_type, Ty::fun(dyn_tensor_type, maybe_tensor));
Ty::Forall(vec![shape_var, a_var], Box::new(fun_type))
}
#[must_use]
pub fn with_dyn_shape_type(tensor_tycon: &TyCon) -> Ty {
let a_var = TyVar::new(0, Kind::Star);
let r_var = TyVar::new(1, Kind::Star);
let shape_var = TyVar::new(2, Kind::nat_list());
let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
let tensor_type = Ty::App(
Box::new(Ty::App(
Box::new(Ty::Con(tensor_tycon.clone())),
Box::new(Ty::TyList(TyList::Var(shape_var.clone()))),
)),
Box::new(Ty::Var(a_var.clone())),
);
let continuation = Ty::Forall(
vec![shape_var],
Box::new(Ty::fun(tensor_type, Ty::Var(r_var.clone()))),
);
let fun_type = Ty::fun(
dyn_tensor_type,
Ty::fun(continuation, Ty::Var(r_var.clone())),
);
Ty::Forall(vec![a_var, r_var], Box::new(fun_type))
}
#[must_use]
pub fn dyn_shape_type(int_tycon: &TyCon) -> Ty {
let a_var = TyVar::new(0, Kind::Star);
let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
let int_list = Ty::List(Box::new(Ty::Con(int_tycon.clone())));
let fun_type = Ty::fun(dyn_tensor_type, int_list);
Ty::Forall(vec![a_var], Box::new(fun_type))
}
#[must_use]
pub fn dyn_rank_type(int_tycon: &TyCon) -> Ty {
let a_var = TyVar::new(0, Kind::Star);
let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
let fun_type = Ty::fun(dyn_tensor_type, Ty::Con(int_tycon.clone()));
Ty::Forall(vec![a_var], Box::new(fun_type))
}
#[must_use]
pub fn static_shape_witness(dims: &[u64]) -> Ty {
let shape = TyList::shape_from_dims(dims);
shape_witness_of(shape)
}
#[must_use]
pub fn is_dyn_tensor(ty: &Ty) -> bool {
match ty {
Ty::App(f, _) => matches!(f.as_ref(), Ty::Con(c) if c.name.as_str() == "DynTensor"),
_ => false,
}
}
#[must_use]
pub fn dyn_tensor_elem_type(ty: &Ty) -> Option<&Ty> {
match ty {
Ty::App(f, elem) => {
if matches!(f.as_ref(), Ty::Con(c) if c.name.as_str() == "DynTensor") {
Some(elem)
} else {
None
}
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dyn_tensor_tycon() {
let tycon = dyn_tensor_tycon();
assert_eq!(tycon.name.as_str(), "DynTensor");
assert_eq!(tycon.kind, Kind::star_to_star());
}
#[test]
fn test_shape_witness_tycon() {
let tycon = shape_witness_tycon();
assert_eq!(tycon.name.as_str(), "ShapeWitness");
match &tycon.kind {
Kind::Arrow(from, to) => {
assert_eq!(**from, Kind::nat_list());
assert_eq!(**to, Kind::Star);
}
_ => panic!("expected arrow kind"),
}
}
#[test]
fn test_dyn_tensor_of() {
let float_ty = Ty::Con(TyCon::new(Symbol::intern("Float"), Kind::Star));
let dyn_ty = dyn_tensor_of(float_ty.clone());
assert!(is_dyn_tensor(&dyn_ty));
assert_eq!(dyn_tensor_elem_type(&dyn_ty), Some(&float_ty));
}
#[test]
fn test_static_shape_witness() {
let witness = static_shape_witness(&[1024, 768]);
match &witness {
Ty::App(f, arg) => {
assert!(matches!(f.as_ref(), Ty::Con(c) if c.name.as_str() == "ShapeWitness"));
match arg.as_ref() {
Ty::TyList(list) => {
let dims = list.to_static_dims().unwrap();
assert_eq!(dims, vec![1024, 768]);
}
_ => panic!("expected TyList"),
}
}
_ => panic!("expected App"),
}
}
#[test]
fn test_is_dyn_tensor() {
let float_ty = Ty::Con(TyCon::new(Symbol::intern("Float"), Kind::Star));
let dyn_ty = dyn_tensor_of(float_ty);
assert!(is_dyn_tensor(&dyn_ty));
let non_dyn = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
assert!(!is_dyn_tensor(&non_dyn));
}
#[test]
fn test_to_dynamic_type() {
let tensor_tycon = TyCon::new(Symbol::intern("Tensor"), Kind::tensor_kind());
let ty = to_dynamic_type(&tensor_tycon);
match ty {
Ty::Forall(vars, _body) => {
assert_eq!(vars.len(), 2);
assert_eq!(vars[0].kind, Kind::nat_list()); assert_eq!(vars[1].kind, Kind::Star); }
_ => panic!("expected forall type"),
}
}
#[test]
fn test_from_dynamic_type() {
let tensor_tycon = TyCon::new(Symbol::intern("Tensor"), Kind::tensor_kind());
let maybe_tycon = TyCon::new(Symbol::intern("Maybe"), Kind::star_to_star());
let ty = from_dynamic_type(&tensor_tycon, &maybe_tycon);
match ty {
Ty::Forall(vars, _body) => {
assert_eq!(vars.len(), 2);
}
_ => panic!("expected forall type"),
}
}
}