frunk_utils/
lib.rs

1//! Utilities for working with frunk.
2
3use frunk::{
4    hlist::{HMappable, HZippable},
5    prelude::HList,
6    Generic, HCons, HNil, LabelledGeneric,
7};
8
9pub use self::cons_list::{ConsList, ConsListT};
10
11pub mod arithmetic_macros;
12pub mod cons_list;
13
14/// The Func trait from frunk doesn't take `self` as a parameter to `call` so there isn't an easy way to get context
15/// from the surrounding scope. Here we define our own `Poly` wrapper and `Func` trait that does take `self` as a
16/// parameter so the caller can include whatever context they need.
17pub struct Poly<F>(pub F);
18
19pub trait Func<I> {
20    type Output;
21
22    fn call(&mut self, i: I) -> Self::Output;
23}
24
25impl<F: Func<I>, I> Func<I> for &mut F {
26    type Output = F::Output;
27
28    fn call(&mut self, i: I) -> Self::Output {
29        (*self).call(i)
30    }
31}
32
33impl<F: Func<Head>, Head, Tail: HMappable<Poly<F>>> HMappable<Poly<F>> for HCons<Head, Tail> {
34    type Output = HCons<<F as Func<Head>>::Output, <Tail as HMappable<Poly<F>>>::Output>;
35
36    fn map(self, mut mapper: Poly<F>) -> Self::Output {
37        let HCons { head, tail } = self;
38        HCons {
39            head: mapper.0.call(head),
40            tail: tail.map(mapper),
41        }
42    }
43}
44
45/// Convenience functions for the caller to map between similarly-shaped types implementing [Generic] without having to
46/// explicitly call [Generic::from] and [Generic::into]
47pub trait WithGeneric: Generic {
48    fn hmap<U: Generic, F>(self, f: F) -> U
49    where
50        Self::Repr: HMappable<Poly<F>, Output = U::Repr>;
51
52    fn hzip<U: Generic, TU: Generic<Repr = <Self::Repr as HZippable<U::Repr>>::Zipped>>(
53        self,
54        other: U,
55    ) -> TU
56    where
57        Self::Repr: HZippable<U::Repr>;
58
59    fn hzip_with<
60        F,
61        U: Generic,
62        TU: Generic<Repr = <<Self::Repr as HZippable<U::Repr>>::Zipped as HMappable<Poly<F>>>::Output>,
63    >(
64        self,
65        other: U,
66        f: F,
67    ) -> TU
68    where
69        Self::Repr: HZippable<U::Repr>,
70        <Self::Repr as HZippable<U::Repr>>::Zipped: HMappable<Poly<F>>;
71
72    fn hzipped_for_each<F, U: Generic>(self, other: U, f: F)
73    where
74        Self::Repr: HZippable<U::Repr>,
75        <Self::Repr as HZippable<U::Repr>>::Zipped: ForEach<F>;
76
77    fn hzip_to_list<F, U: Generic, O>(
78        self,
79        other: U,
80        f: F,
81    ) -> ConsList<O, <<Self::Repr as HZippable<U::Repr>>::Zipped as MapToList<F, O>>::Output>
82    where
83        Self::Repr: HZippable<U::Repr>,
84        <Self::Repr as HZippable<U::Repr>>::Zipped: MapToList<F, O>;
85
86    fn map_to_list<F, O>(self, f: F) -> ConsList<O, <Self::Repr as MapToList<F, O>>::Output>
87    where
88        Self::Repr: MapToList<F, O>;
89
90    fn for_each<F>(self, f: F)
91    where
92        Self::Repr: ForEach<F>;
93
94    /// Allows getting an iterator over the fields of a struct if they all have the same type
95    fn fields_into_iter<U>(self) -> impl Iterator<Item = U>
96    where
97        Self::Repr: MapToList<Identity, U>;
98}
99
100pub struct Identity;
101
102impl<T> Func<T> for Identity {
103    type Output = T;
104
105    fn call(&mut self, i: T) -> Self::Output {
106        i
107    }
108}
109
110impl<T: Generic> WithGeneric for T {
111    fn hmap<U: Generic, F>(self, f: F) -> U
112    where
113        Self::Repr: HMappable<Poly<F>, Output = U::Repr>,
114    {
115        Generic::from(Generic::into(self).map(Poly(f)))
116    }
117
118    fn hzip<U: Generic, TU: Generic<Repr = <Self::Repr as HZippable<U::Repr>>::Zipped>>(
119        self,
120        other: U,
121    ) -> TU
122    where
123        Self::Repr: HZippable<U::Repr>,
124    {
125        Generic::from(Generic::into(self).zip(Generic::into(other)))
126    }
127
128    fn hzip_with<
129        F,
130        U: Generic,
131        TU: Generic<Repr = <<Self::Repr as HZippable<U::Repr>>::Zipped as HMappable<Poly<F>>>::Output>,
132    >(
133        self,
134        other: U,
135        f: F,
136    ) -> TU
137    where
138        Self::Repr: HZippable<U::Repr>,
139        <Self::Repr as HZippable<U::Repr>>::Zipped: HMappable<Poly<F>>,
140    {
141        Generic::from(Generic::into(self).zip(Generic::into(other)).map(Poly(f)))
142    }
143
144    fn hzipped_for_each<F, U: Generic>(self, other: U, f: F)
145    where
146        Self::Repr: HZippable<U::Repr>,
147        <Self::Repr as HZippable<U::Repr>>::Zipped: ForEach<F>,
148    {
149        Generic::into(self).zip(Generic::into(other)).for_each(f);
150    }
151
152    fn hzip_to_list<F, U: Generic, O>(
153        self,
154        other: U,
155        f: F,
156    ) -> ConsList<O, <<Self::Repr as HZippable<U::Repr>>::Zipped as MapToList<F, O>>::Output>
157    where
158        Self::Repr: HZippable<U::Repr>,
159        <Self::Repr as HZippable<U::Repr>>::Zipped: MapToList<F, O>,
160    {
161        Generic::into(self).zip(Generic::into(other)).map_to_list(f)
162    }
163
164    fn map_to_list<F, O>(self, f: F) -> ConsList<O, <Self::Repr as MapToList<F, O>>::Output>
165    where
166        Self::Repr: MapToList<F, O>,
167    {
168        Generic::into(self).map_to_list(f)
169    }
170
171    fn for_each<F>(self, f: F)
172    where
173        Self::Repr: ForEach<F>,
174    {
175        Generic::into(self).for_each(f)
176    }
177
178    fn fields_into_iter<U>(self) -> impl Iterator<Item = U>
179    where
180        Self::Repr: MapToList<Identity, U>,
181    {
182        self.map_to_list(Identity).into_iter()
183    }
184}
185
186/// Convenience functions for the caller to map between similarly-shaped types implementing [LabelledGeneric] without
187/// having to explicitly call [LabelledGeneric::from] and [LabelledGeneric::into]
188pub trait WithLabelledGeneric: LabelledGeneric {
189    fn hmap<U: LabelledGeneric, F>(self, f: F) -> U
190    where
191        Self::Repr: HMappable<Poly<F>, Output = U::Repr>;
192
193    fn hzip<
194        U: LabelledGeneric,
195        TU: LabelledGeneric<Repr = <Self::Repr as HZippable<U::Repr>>::Zipped>,
196    >(
197        self,
198        other: U,
199    ) -> TU
200    where
201        Self::Repr: HZippable<U::Repr>;
202
203    fn map_to_list<F, U>(self, f: F) -> ConsList<U, <Self::Repr as MapToList<F, U>>::Output>
204    where
205        Self::Repr: MapToList<F, U>;
206
207    fn for_each<F>(self, f: F)
208    where
209        Self::Repr: ForEach<F>;
210}
211
212impl<T: LabelledGeneric> WithLabelledGeneric for T {
213    fn hmap<U: LabelledGeneric, F>(self, f: F) -> U
214    where
215        Self::Repr: HMappable<Poly<F>, Output = U::Repr>,
216    {
217        LabelledGeneric::from(LabelledGeneric::into(self).map(Poly(f)))
218    }
219
220    fn hzip<
221        U: LabelledGeneric,
222        TU: LabelledGeneric<Repr = <Self::Repr as HZippable<U::Repr>>::Zipped>,
223    >(
224        self,
225        other: U,
226    ) -> TU
227    where
228        Self::Repr: HZippable<U::Repr>,
229    {
230        LabelledGeneric::from(LabelledGeneric::into(self).zip(LabelledGeneric::into(other)))
231    }
232
233    fn map_to_list<F, U>(self, f: F) -> ConsList<U, <Self::Repr as MapToList<F, U>>::Output>
234    where
235        Self::Repr: MapToList<F, U>,
236    {
237        LabelledGeneric::into(self).map_to_list(f)
238    }
239
240    fn for_each<F>(self, f: F)
241    where
242        Self::Repr: ForEach<F>,
243    {
244        LabelledGeneric::into(self).for_each(f)
245    }
246}
247
248pub trait MapToList<F, U>: HList {
249    type Output: ConsListT<U>;
250
251    /// Map a monomorphizing function over the HList to produce an [iterable](`ConsList::into_iter`) datastructure which
252    /// lives fully on stack
253    fn map_to_list(self, f: F) -> ConsList<U, Self::Output>;
254}
255
256impl<F, U> MapToList<F, U> for HNil {
257    type Output = cons_list::Nil;
258
259    fn map_to_list(self, _f: F) -> ConsList<U, Self::Output> {
260        ConsList::nil()
261    }
262}
263
264impl<F: Func<Head, Output = U>, U, Head, Tail: MapToList<F, U>> MapToList<F, U>
265    for HCons<Head, Tail>
266{
267    type Output = cons_list::Cons<U, <Tail as MapToList<F, U>>::Output>;
268
269    fn map_to_list(self, mut f: F) -> ConsList<U, Self::Output> {
270        let HCons { head, tail } = self;
271        ConsList::cons(f.call(head), tail.map_to_list(f))
272    }
273}
274
275pub trait ForEach<F>: HList {
276    fn for_each(self, f: F);
277}
278
279impl<F> ForEach<F> for HNil {
280    fn for_each(self, _: F) {}
281}
282
283impl<F: Func<Head, Output = ()>, Head, Tail: ForEach<F>> ForEach<F> for HCons<Head, Tail> {
284    fn for_each(self, mut f: F) {
285        let HCons { head, tail } = self;
286        f.call(head);
287        tail.for_each(f)
288    }
289}