1use crate::{Kind, Ty, TyCon, TyList, TyVar};
44use bhc_intern::Symbol;
45
46#[must_use]
56pub fn dyn_tensor_tycon() -> TyCon {
57 TyCon::new(Symbol::intern("DynTensor"), Kind::star_to_star())
58}
59
60#[must_use]
69pub fn dyn_tensor_of(elem_ty: Ty) -> Ty {
70 Ty::App(Box::new(Ty::Con(dyn_tensor_tycon())), Box::new(elem_ty))
71}
72
73#[must_use]
82pub fn shape_witness_tycon() -> TyCon {
83 let kind = Kind::Arrow(Box::new(Kind::nat_list()), Box::new(Kind::Star));
85 TyCon::new(Symbol::intern("ShapeWitness"), kind)
86}
87
88#[must_use]
90pub fn shape_witness_of(shape: TyList) -> Ty {
91 Ty::App(
92 Box::new(Ty::Con(shape_witness_tycon())),
93 Box::new(Ty::TyList(shape)),
94 )
95}
96
97#[must_use]
103pub fn to_dynamic_type(tensor_tycon: &TyCon) -> Ty {
104 let shape_var = TyVar::new(0, Kind::nat_list());
105 let a_var = TyVar::new(1, Kind::Star);
106
107 let tensor_type = Ty::App(
109 Box::new(Ty::App(
110 Box::new(Ty::Con(tensor_tycon.clone())),
111 Box::new(Ty::TyList(TyList::Var(shape_var.clone()))),
112 )),
113 Box::new(Ty::Var(a_var.clone())),
114 );
115
116 let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
118
119 let fun_type = Ty::fun(tensor_type, dyn_tensor_type);
121
122 Ty::Forall(vec![shape_var, a_var], Box::new(fun_type))
124}
125
126#[must_use]
135pub fn from_dynamic_type(tensor_tycon: &TyCon, maybe_tycon: &TyCon) -> Ty {
136 let shape_var = TyVar::new(0, Kind::nat_list());
137 let a_var = TyVar::new(1, Kind::Star);
138
139 let witness_type = shape_witness_of(TyList::Var(shape_var.clone()));
141
142 let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
144
145 let tensor_type = Ty::App(
147 Box::new(Ty::App(
148 Box::new(Ty::Con(tensor_tycon.clone())),
149 Box::new(Ty::TyList(TyList::Var(shape_var.clone()))),
150 )),
151 Box::new(Ty::Var(a_var.clone())),
152 );
153
154 let maybe_tensor = Ty::App(
156 Box::new(Ty::Con(maybe_tycon.clone())),
157 Box::new(tensor_type),
158 );
159
160 let fun_type = Ty::fun(witness_type, Ty::fun(dyn_tensor_type, maybe_tensor));
162
163 Ty::Forall(vec![shape_var, a_var], Box::new(fun_type))
165}
166
167#[must_use]
175pub fn with_dyn_shape_type(tensor_tycon: &TyCon) -> Ty {
176 let a_var = TyVar::new(0, Kind::Star);
177 let r_var = TyVar::new(1, Kind::Star);
178 let shape_var = TyVar::new(2, Kind::nat_list());
179
180 let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
182
183 let tensor_type = Ty::App(
185 Box::new(Ty::App(
186 Box::new(Ty::Con(tensor_tycon.clone())),
187 Box::new(Ty::TyList(TyList::Var(shape_var.clone()))),
188 )),
189 Box::new(Ty::Var(a_var.clone())),
190 );
191
192 let continuation = Ty::Forall(
194 vec![shape_var],
195 Box::new(Ty::fun(tensor_type, Ty::Var(r_var.clone()))),
196 );
197
198 let fun_type = Ty::fun(
200 dyn_tensor_type,
201 Ty::fun(continuation, Ty::Var(r_var.clone())),
202 );
203
204 Ty::Forall(vec![a_var, r_var], Box::new(fun_type))
206}
207
208#[must_use]
216pub fn dyn_shape_type(int_tycon: &TyCon) -> Ty {
217 let a_var = TyVar::new(0, Kind::Star);
218
219 let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
221
222 let int_list = Ty::List(Box::new(Ty::Con(int_tycon.clone())));
224
225 let fun_type = Ty::fun(dyn_tensor_type, int_list);
227
228 Ty::Forall(vec![a_var], Box::new(fun_type))
230}
231
232#[must_use]
238pub fn dyn_rank_type(int_tycon: &TyCon) -> Ty {
239 let a_var = TyVar::new(0, Kind::Star);
240
241 let dyn_tensor_type = dyn_tensor_of(Ty::Var(a_var.clone()));
243
244 let fun_type = Ty::fun(dyn_tensor_type, Ty::Con(int_tycon.clone()));
246
247 Ty::Forall(vec![a_var], Box::new(fun_type))
249}
250
251#[must_use]
260pub fn static_shape_witness(dims: &[u64]) -> Ty {
261 let shape = TyList::shape_from_dims(dims);
262 shape_witness_of(shape)
263}
264
265#[must_use]
267pub fn is_dyn_tensor(ty: &Ty) -> bool {
268 match ty {
269 Ty::App(f, _) => matches!(f.as_ref(), Ty::Con(c) if c.name.as_str() == "DynTensor"),
270 _ => false,
271 }
272}
273
274#[must_use]
276pub fn dyn_tensor_elem_type(ty: &Ty) -> Option<&Ty> {
277 match ty {
278 Ty::App(f, elem) => {
279 if matches!(f.as_ref(), Ty::Con(c) if c.name.as_str() == "DynTensor") {
280 Some(elem)
281 } else {
282 None
283 }
284 }
285 _ => None,
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use super::*;
292
293 #[test]
294 fn test_dyn_tensor_tycon() {
295 let tycon = dyn_tensor_tycon();
296 assert_eq!(tycon.name.as_str(), "DynTensor");
297 assert_eq!(tycon.kind, Kind::star_to_star());
298 }
299
300 #[test]
301 fn test_shape_witness_tycon() {
302 let tycon = shape_witness_tycon();
303 assert_eq!(tycon.name.as_str(), "ShapeWitness");
304 match &tycon.kind {
305 Kind::Arrow(from, to) => {
306 assert_eq!(**from, Kind::nat_list());
307 assert_eq!(**to, Kind::Star);
308 }
309 _ => panic!("expected arrow kind"),
310 }
311 }
312
313 #[test]
314 fn test_dyn_tensor_of() {
315 let float_ty = Ty::Con(TyCon::new(Symbol::intern("Float"), Kind::Star));
316 let dyn_ty = dyn_tensor_of(float_ty.clone());
317
318 assert!(is_dyn_tensor(&dyn_ty));
319 assert_eq!(dyn_tensor_elem_type(&dyn_ty), Some(&float_ty));
320 }
321
322 #[test]
323 fn test_static_shape_witness() {
324 let witness = static_shape_witness(&[1024, 768]);
325 match &witness {
326 Ty::App(f, arg) => {
327 assert!(matches!(f.as_ref(), Ty::Con(c) if c.name.as_str() == "ShapeWitness"));
328 match arg.as_ref() {
329 Ty::TyList(list) => {
330 let dims = list.to_static_dims().unwrap();
331 assert_eq!(dims, vec![1024, 768]);
332 }
333 _ => panic!("expected TyList"),
334 }
335 }
336 _ => panic!("expected App"),
337 }
338 }
339
340 #[test]
341 fn test_is_dyn_tensor() {
342 let float_ty = Ty::Con(TyCon::new(Symbol::intern("Float"), Kind::Star));
343 let dyn_ty = dyn_tensor_of(float_ty);
344 assert!(is_dyn_tensor(&dyn_ty));
345
346 let non_dyn = Ty::Con(TyCon::new(Symbol::intern("Int"), Kind::Star));
347 assert!(!is_dyn_tensor(&non_dyn));
348 }
349
350 #[test]
351 fn test_to_dynamic_type() {
352 let tensor_tycon = TyCon::new(Symbol::intern("Tensor"), Kind::tensor_kind());
353 let ty = to_dynamic_type(&tensor_tycon);
354
355 match ty {
357 Ty::Forall(vars, _body) => {
358 assert_eq!(vars.len(), 2);
359 assert_eq!(vars[0].kind, Kind::nat_list()); assert_eq!(vars[1].kind, Kind::Star); }
362 _ => panic!("expected forall type"),
363 }
364 }
365
366 #[test]
367 fn test_from_dynamic_type() {
368 let tensor_tycon = TyCon::new(Symbol::intern("Tensor"), Kind::tensor_kind());
369 let maybe_tycon = TyCon::new(Symbol::intern("Maybe"), Kind::star_to_star());
370 let ty = from_dynamic_type(&tensor_tycon, &maybe_tycon);
371
372 match ty {
374 Ty::Forall(vars, _body) => {
375 assert_eq!(vars.len(), 2);
376 }
377 _ => panic!("expected forall type"),
378 }
379 }
380}