Skip to main content

reflect_nat/
hlist.rs

1//! Type-level heterogeneous lists with [`Reflect`] implementations.
2
3use reify_reflect_core::{Reflect, RuntimeValue};
4use std::marker::PhantomData;
5
6/// The empty heterogeneous list.
7///
8/// # Examples
9///
10/// ```
11/// use reflect_nat::HNil;
12/// use reify_reflect_core::Reflect;
13///
14/// assert_eq!(HNil::reflect(), Vec::<reify_reflect_core::RuntimeValue>::new());
15/// ```
16pub struct HNil;
17
18/// A cons cell for heterogeneous lists. `HCons<H, T>` prepends head `H` to tail `T`.
19///
20/// # Examples
21///
22/// ```
23/// use reflect_nat::{HNil, HCons, Z, S};
24/// use reify_reflect_core::{Reflect, RuntimeValue};
25///
26/// // A list containing [1, 0] at the type level
27/// type MyList = HCons<S<Z>, HCons<Z, HNil>>;
28/// assert_eq!(
29///     MyList::reflect(),
30///     vec![RuntimeValue::Nat(1), RuntimeValue::Nat(0)]
31/// );
32/// ```
33pub struct HCons<H, T>(PhantomData<(H, T)>);
34
35/// Marker trait for type-level heterogeneous lists.
36///
37/// # Examples
38///
39/// ```
40/// use reflect_nat::{HNil, HCons, HList, Z, S};
41///
42/// fn require_hlist<L: HList>() -> usize { L::len() }
43/// assert_eq!(require_hlist::<HNil>(), 0);
44/// assert_eq!(require_hlist::<HCons<Z, HCons<S<Z>, HNil>>>(), 2);
45/// ```
46pub trait HList {
47    /// The number of elements in this type-level list.
48    fn len() -> usize;
49
50    /// Whether this type-level list is empty.
51    fn is_empty() -> bool {
52        Self::len() == 0
53    }
54}
55
56impl HList for HNil {
57    fn len() -> usize {
58        0
59    }
60}
61
62impl<H, T: HList> HList for HCons<H, T> {
63    fn len() -> usize {
64        1 + T::len()
65    }
66}
67
68impl Reflect for HNil {
69    type Value = Vec<RuntimeValue>;
70
71    fn reflect() -> Self::Value {
72        vec![]
73    }
74}
75
76impl<H, T> Reflect for HCons<H, T>
77where
78    H: Reflect<Value = RuntimeValue>,
79    T: Reflect<Value = Vec<RuntimeValue>>,
80{
81    type Value = Vec<RuntimeValue>;
82
83    fn reflect() -> Self::Value {
84        let mut list = vec![H::reflect()];
85        list.extend(T::reflect());
86        list
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::{S, Z};
94
95    #[test]
96    fn hnil_reflects_to_empty_list() {
97        assert_eq!(HNil::reflect(), Vec::<RuntimeValue>::new());
98    }
99
100    #[test]
101    fn single_element_list() {
102        type L = HCons<Z, HNil>;
103        assert_eq!(L::reflect(), vec![RuntimeValue::Nat(0)]);
104    }
105
106    #[test]
107    fn multi_element_list() {
108        type L = HCons<S<S<Z>>, HCons<S<Z>, HCons<Z, HNil>>>;
109        assert_eq!(
110            L::reflect(),
111            vec![
112                RuntimeValue::Nat(2),
113                RuntimeValue::Nat(1),
114                RuntimeValue::Nat(0),
115            ]
116        );
117    }
118
119    #[test]
120    fn hlist_len() {
121        assert_eq!(HNil::len(), 0);
122        assert_eq!(<HCons<Z, HNil>>::len(), 1);
123        assert_eq!(<HCons<Z, HCons<Z, HCons<Z, HNil>>>>::len(), 3);
124    }
125
126    #[test]
127    fn hlist_is_empty() {
128        assert!(HNil::is_empty());
129        assert!(!<HCons<Z, HNil>>::is_empty());
130    }
131
132    #[test]
133    fn round_trip_type_level_to_runtime() {
134        // Construct at type level, reflect, assert runtime values
135        type List = HCons<S<S<S<Z>>>, HCons<S<Z>, HCons<Z, HNil>>>;
136        let reflected = List::reflect();
137        assert_eq!(reflected.len(), 3);
138        assert_eq!(reflected[0], RuntimeValue::Nat(3));
139        assert_eq!(reflected[1], RuntimeValue::Nat(1));
140        assert_eq!(reflected[2], RuntimeValue::Nat(0));
141    }
142}