tlist/
lib.rs

1#![forbid(unsafe_code)]
2#![no_std]
3
4#![cfg_attr(feature = "doc", feature(doc_auto_cfg))]
5#![doc = include_str!("../README.md")]
6
7#[cfg(feature = "typenum")]
8mod typenum_ext;
9
10mod sealed {
11    pub trait Sealed {}
12}
13use sealed::Sealed;
14
15#[cfg(feature = "typenum")]
16use typenum_ext::UnsignedExt;
17
18use core::marker::PhantomData;
19
20#[doc(hidden)]
21pub trait TListImpl {
22    type Last<X>;
23    type Inits<X>: TList;
24}
25
26/// Type-level lists.
27///
28/// This trait is a "type-level enum".
29/// [trait@TList], [TNil] and [TCons] are the type-level equivalent of the following value-level enum:
30///
31/// ```ignore
32/// pub enum List<H, T> {
33///   Nil,
34///   Cons(H, T),
35/// }
36/// ```
37///
38/// This trait can be considered a 'marker' trait:
39/// It has no methods to be called at runtime.
40/// It only has some GATs.
41///
42/// Rather than calling these GATs directly,
43/// it is recommended to instead use their type aliases,
44/// as calling those is less verbose:
45///
46/// ```rust
47/// use tlist::*;
48///
49/// // This is possible...
50/// type Fine = <TList![u8, u16] as TList>::Concat<TList![u32, u64]>;
51///
52/// // But this is more readable:
53/// type Nice = Concat<TList![u8, u16], TList![u32, u64]>;
54///
55/// static_assertions::assert_type_eq_all!(Fine, Nice);
56/// ```
57pub trait TList: Sealed + TListImpl {
58    /// Implementation of [type@Concat].
59    type Concat<Rhs: TList>: TList;
60
61    /// Implementation of [type@Reverse].
62    type Reverse: TList;
63
64    /// True iff the list is empty, false otherwise.
65    ///
66    /// Returns a bool as associated const value.
67    /// If you'd rather use a type, see [IsEmpty]
68    /// (which needs `typenum` feature to be enabled)
69    ///
70    /// Also see the [Empty] and [NonEmpty] traits.
71    const IS_EMPTY: bool;
72    /// The amount of elements in the list.
73    ///
74    /// Returns an usize as associated const value.
75    /// If you'd rather use a type, see [Len]
76    /// (which needs `typenum` feature to be enabled)
77    const LEN: usize;
78
79    /// Implementation of [type@IsEmpty].
80    #[cfg(feature = "typenum")]
81    type IsEmpty: Bit;
82
83    /// Implementation of [type@Len].
84    #[cfg(feature = "typenum")]
85    type Len: UnsignedExt;
86}
87
88/// The empty [trait@TList].
89///
90/// Only [TNil] implements this constraining trait.
91///
92/// See also [TList::IS_EMPTY] and [IsEmpty] if you want work with both [Empty] and [NonEmpty]
93/// lists generically.
94pub trait Empty: TList + Sealed {}
95
96/// Non-empty [trait@TList]s.
97///
98/// Any [trait@TList] except [TNil] implements this constraining trait.
99/// (In other words: Any [`TCons<H, T>`], regardless of what `H` or `T` it contains, implements it.)
100///
101/// Quite a number of operations are only defined for non-empty [trait@TList]s,
102/// so this constraint is used a lot in the library itself as well.
103///
104/// See also [TList::IS_EMPTY] and [IsEmpty] if you want work with both [Empty] and [NonEmpty]
105/// lists generically.
106pub trait NonEmpty: TList + Sealed {
107    /// Implementation of [type@First].
108    type First;
109    /// Implementation of [type@Rest].
110    type Rest: TList;
111    /// Implementation of [type@Last].
112    type Last;
113    /// Implementation of [type@Inits].
114    type Inits: TList;
115}
116
117/// The empty TList.
118#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
119pub struct TNil;
120
121/// A non-empty TList whose first element is `H` and whose tail is the TList `T`.
122#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
123pub struct TCons<H, T: TList>(PhantomData<(H, T)>);
124
125impl Sealed for TNil {}
126impl<H, T: TList> Sealed for TCons<H, T> {}
127
128impl TListImpl for TNil {
129    type Last<X> = X;
130    type Inits<X> = TNil;
131}
132impl TList for TNil {
133    type Concat<Rhs: TList> = Rhs;
134    type Reverse = TNil;
135    #[cfg(feature = "typenum")]
136    type IsEmpty = B1;
137    #[cfg(feature = "typenum")]
138    type Len = U0;
139    const IS_EMPTY: bool = true;
140    const LEN: usize = 0;
141}
142
143impl Empty for TNil {}
144
145impl<H, T: TList> TListImpl for TCons<H, T> {
146    type Last<X> = T::Last<H>;
147    type Inits<X> = TCons<X, T::Inits<H>>;
148}
149impl<H, T: TList> TList for TCons<H, T> {
150    type Concat<Rhs: TList> = TCons<H, T::Concat<Rhs>>;
151    type Reverse = Concat<T::Reverse, TCons<H, TNil>>;
152    #[cfg(feature = "typenum")]
153    type IsEmpty = B0;
154    #[cfg(feature = "typenum")]
155    type Len = <T::Len as UnsignedExt>::Succ;
156    const IS_EMPTY: bool = false;
157    const LEN: usize = {
158        T::LEN + 1
159    };
160}
161
162impl<H, T: TList> NonEmpty for TCons<H, T> {
163    type First = H;
164    type Rest = T;
165    type Last = T::Last<H>;
166    type Inits = T::Inits<H>;
167}
168
169/// Shorthand macro to construct TList types.
170///
171/// This is usually much more readable than writing out the nesting of
172/// [TCons] and [TNil] by hand.
173///
174/// ```rust
175/// use tlist::*;
176///
177/// use static_assertions::assert_type_eq_all as type_eq;
178/// use typenum::consts::{U1, U2, U3, U4, U42};
179///
180/// type_eq!(TList![], TNil);
181///
182/// type_eq!(TList![U42], TCons<U42, TNil>);
183///
184/// type_eq!(TList![U1, U2, U3], TCons<U1, TCons<U2, TCons<U3, TNil>>>);
185///
186/// // You can also use `...Rest` for the last argument:
187/// type Rest = TList![U3, U4];
188/// type_eq!(TList![U1, U2, ...Rest], TCons<U1, TCons<U2, Rest>>);
189/// ```
190#[macro_export]
191// Implementation based on the frunk crate's HList! macro.
192macro_rules! TList {
193    () => { $crate::TNil };
194    (...$Rest:ty) => { $Rest };
195    ($A:ty) => { $crate::TList![$A,] };
196    ($A:ty, $($tok:tt)*) => {
197        $crate::TCons<$A, $crate::TList![$($tok)*]>
198    };
199}
200
201/// Type-level 'function' to return the first element of a TList
202///
203/// Only implemented for non-empty TLists.
204///
205/// ```rust
206/// use tlist::*;
207/// use typenum::consts::{U1, U2, U3};
208/// use static_assertions::assert_type_eq_all as assert_type_eq;
209///
210/// assert_type_eq!(First<TList![U1, U2, U3]>, U1);
211///
212/// assert_type_eq!(First<TList![i8, usize, i32, u64]>, i8);
213/// ```
214pub type First<List> = <List as NonEmpty>::First;
215
216/// Type-level 'function' to return the first element of a TList
217///
218/// Only implemented for non-empty TLists.
219///
220/// ```rust
221/// use tlist::*;
222/// use typenum::consts::{U1, U2, U3};
223/// use static_assertions::assert_type_eq_all as assert_type_eq;
224///
225/// assert_type_eq!(Rest<TList![U1, U2, U3]>, TList![U2, U3]);
226///
227/// assert_type_eq!(Rest<TList![i8, usize, i32, u64]>, TList![usize, i32, u64]);
228/// ```
229pub type Rest<List> = <List as NonEmpty>::Rest;
230
231/// Type-level 'function' to return the all elements but the last element of a TList
232///
233/// Only implemented for non-empty TLists.
234/// ```rust
235/// use tlist::*;
236/// use typenum::consts::{U1, U2, U3};
237/// use static_assertions::assert_type_eq_all as assert_type_eq;
238///
239/// assert_type_eq!(Last<TList![U1, U2, U3]>, U3);
240///
241/// assert_type_eq!(Last<TList![i8, usize, i32, u64]>, u64);
242/// ```
243pub type Last<List> = <List as NonEmpty>::Last;
244
245/// Type-level 'function' to return the all elements but the last element of a TList
246///
247/// Only implemented for non-empty TLists.
248/// ```rust
249/// use tlist::*;
250/// use typenum::consts::{U1, U2, U3};
251/// use static_assertions::assert_type_eq_all as assert_type_eq;
252///
253/// assert_type_eq!(Inits<TList![U1, U2, U3]>, TList![U1, U2]);
254///
255/// assert_type_eq!(Inits<TList![i8, usize, i32, u64]>, TList![i8, usize, i32]);
256/// ```
257pub type Inits<List> = <List as NonEmpty>::Inits;
258
259/// Type-level 'function' to concatenate two TLists.
260///
261///
262/// ```rust
263/// use tlist::*;
264/// use typenum::consts::{U1, U2, U3, U4, U5};
265/// use static_assertions::assert_type_eq_all as assert_type_eq;
266///
267/// assert_type_eq!(Concat<TList![], TList![]>, TList![]);
268///
269/// assert_type_eq!(Concat<TList![U1], TList![]>, TList![U1]);
270///
271/// assert_type_eq!(Concat<TList![U2], TList![]>, TList![U2]);
272///
273/// assert_type_eq!(Concat<TList![U1, U2], TList![U3, U4, U5]>, TList![U1, U2, U3, U4, U5]);
274/// ```
275///
276pub type Concat<Lhs, Rhs> = <Lhs as TList>::Concat<Rhs>;
277
278/// Type-level 'function' to reverse a TList.
279///
280/// ```rust
281/// use tlist::*;
282/// use typenum::consts::{U1, U2, U3, U4, U5};
283/// use static_assertions::assert_type_eq_all as assert_type_eq;
284///
285/// assert_type_eq!(Reverse<TList![]>, TList![]);
286///
287/// assert_type_eq!(Reverse<TList![U3, U2, U1]>, TList![U1, U2, U3]);
288/// ```
289///
290pub type Reverse<List> = <List as TList>::Reverse;
291
292#[cfg(feature = "typenum")]
293use typenum::{Bit, B0, B1, consts::U0};
294/// Type-level 'function' to calculate the length of a TList.
295///
296/// You can turn the result into a `usize` using `Len<List>::USIZE` or `Len<List>::to_usize()`.
297///
298/// (See [`typenum::Unsigned`].)
299#[cfg(feature = "typenum")]
300pub type Len<List> = <List as TList>::Len;
301
302/// Type-level 'function' returning [`typenum::B1`] when the list is empty; [`typenum::B0`] otherwise.
303///
304/// You can turn the result into a `bool` using `IsEmpty<List>::BOOL` or `IsEmpty<List>::to_bool()`.
305///
306/// (See [`typenum::Bit`] for more on this.)
307/// ```rust
308/// use tlist::*;
309/// use typenum::{B0, B1, Bit};
310/// use static_assertions::assert_type_eq_all as assert_type_eq;
311///
312/// assert_type_eq!(IsEmpty<TList![]>, B1);
313/// assert_type_eq!(IsEmpty<TList![i32]>, B0);
314/// assert_type_eq!(IsEmpty<TList![u32, i64]>, B0);
315///
316/// assert_eq!(IsEmpty::<TList![]>::BOOL, true);
317/// assert_eq!(IsEmpty::<TList![&'static str]>::BOOL, false);
318/// ```
319///
320/// [IsEmpty] is a type-level function that works for any [trait@TList], returning a type-level boolean.
321/// If you want to _constrain_ what kind of [trait@TList] is allowed for a certain operation,
322/// use the [Empty] or [NonEmpty] constraining traits.
323#[cfg(feature = "typenum")]
324pub type IsEmpty<List> = <List as TList>::IsEmpty;
325
326/// Constraint which only holds if a TList is a prefix of `Other`.
327///
328/// This is not a type-level 'function', but rather a constraint you can use to make compiler errors more readable.
329///
330/// Since this is a constraint, and only holds for a small subset of [trait@TList]s, it _is_
331/// implemented as a separate trait and not as a GAT.
332///
333/// ```rust
334/// use tlist::*;
335/// use typenum::consts::{U1, U2, U3, U4, U42};
336///
337/// static_assertions::assert_impl_all!(TList![U1, U2]: Prefix<TList![U1, U2, U3, U4]>);
338/// static_assertions::assert_not_impl_any!(TList![U42]: Prefix<TList![U1, U2, U3, U4]>);
339/// ```
340///
341/// Also see [EitherPrefix].
342pub trait Prefix<Other: TList> {}
343
344// prefix [] _ = true
345impl<Other: TList> Prefix<Other> for TNil {}
346
347// prefix (h : ls) (h : rs) == prefix ls rs
348impl<H, Ls: TList, Rs: TList> Prefix<TCons<H, Rs>> for TCons<H, Ls> where Ls: Prefix<Rs> {}
349
350/// Constraint which either holds if a [trait@TList] is a prefix of `Other` or if `Other` is a prefix of this [trait@TList].
351///
352/// Relaxed variant of [Prefix].
353///
354/// ```rust
355/// use tlist::*;
356/// use typenum::consts::{U1, U2, U3, U4, U42};
357///
358/// static_assertions::assert_impl_all!(TList![U1, U2]: EitherPrefix<TList![U1, U2, U3, U4]>);
359/// static_assertions::assert_impl_all!(TList![U1, U2, U3, U4]: EitherPrefix<TList![U1, U2]>);
360/// static_assertions::assert_not_impl_any!(TList![U42]: EitherPrefix<TList![U1, U2, U3, U4]>);
361/// static_assertions::assert_not_impl_any!(TList![U1, U2, U3, U4]: EitherPrefix<TList![U4, U3, U2, U1]>);
362/// ```
363pub trait EitherPrefix<Other: TList> {}
364// eitherPrefix [] [] == true
365impl EitherPrefix<TNil> for TNil {}
366
367// eitherPrefix [] (f : gs) == true
368impl<F, GS: TList> EitherPrefix<TCons<F, GS>> for TNil {}
369
370// eitherPrefix (f : fs) [] == true
371impl<F, FS: TList> EitherPrefix<TNil> for TCons<F, FS> {}
372
373// eitherPrefix (f : fs) (g : gs) == true
374impl<F, FS: TList, GS: TList> EitherPrefix<TCons<F, GS>> for TCons<F, FS> where FS: EitherPrefix<GS> {}
375
376#[cfg(test)]
377pub mod tests {
378    // Since all of this is type-level code,
379    // these tests run at compile-time :-).
380    use super::*;
381    use static_assertions::assert_type_eq_all as assert_type_eq;
382    use typenum::consts::*;
383
384    #[test]
385    fn first() {
386        assert_type_eq!(U1, First<TList![U1, U2]>);
387    }
388
389    #[test]
390    fn rest() {
391        assert_type_eq!(TList![U2], Rest<TList![U1, U2]>);
392    }
393
394    #[test]
395    fn last() {
396        assert_type_eq!(U2, Last<TList![U1, U2]>);
397        assert_type_eq!(U1, Last<TList![U1]>);
398    }
399
400    #[test]
401    fn inits() {
402        assert_type_eq!(TList![U1, U2], Inits<TList![U1, U2, U3]>);
403        assert_type_eq!(TList![], Inits<TList![U10]>);
404    }
405
406    #[test]
407    fn concat() {
408        assert_type_eq!(TList![U1, U2, U3], Concat<TList![U1], TList![U2, U3]>);
409    }
410
411    #[test]
412    fn reverse() {
413        assert_type_eq!(TCons<U3, TCons<U2, TCons<U1, TNil>>>, Reverse<TCons<U1, TCons<U2, TCons<U3, TNil>>>>);
414    }
415
416}
417
418#[cfg(test)]
419#[cfg(feature = "typenum")]
420pub mod typenum_tests {
421    // Since all of this is type-level code,
422    // these tests run at compile-time :-).
423    use super::*;
424    use static_assertions::assert_type_eq_all as assert_type_eq;
425    use typenum::consts::*;
426
427    #[test]
428    fn typenum_len() {
429        assert_type_eq!(U0, Len<TList![]>);
430        assert_type_eq!(U1, Len<TList![usize]>);
431        assert_type_eq!(U2, Len<TList![i32, usize]>);
432        assert_type_eq!(
433            U10,
434            Len<TList![char, char, char, char, char, char, char, char, char, char]>
435        );
436    }
437
438    #[test]
439    fn typenum_is_empty() {
440        assert_type_eq!(B1, IsEmpty<TList![]>);
441        assert_type_eq!(B0, IsEmpty<TList![i32]>);
442    }
443}