fp_library/classes/traversable.rs
1//! Traversable type class.
2//!
3//! This module defines the [`Traversable`] trait, which represents data structures that can be traversed, accumulating results in an applicative context.
4
5use super::{applicative::Applicative, foldable::Foldable, functor::Functor};
6use crate::{Apply, functions::identity, kinds::*};
7
8/// A type class for traversable functors.
9///
10/// `Traversable` functors can be traversed, which accumulates results and effects in some [`Applicative`] context.
11pub trait Traversable: Functor + Foldable {
12 /// Map each element of the [`Traversable`] structure to a computation, evaluate those computations and combine the results into an [`Applicative`] context.
13 ///
14 /// The default implementation is defined in terms of [`sequence`] and [`Functor::map`].
15 ///
16 /// **Note**: This default implementation may be less efficient than a direct implementation because it performs two passes:
17 /// first mapping the function to create an intermediate structure of computations, and then sequencing that structure.
18 /// A direct implementation can often perform the traversal in a single pass without allocating an intermediate container.
19 /// Types should provide their own implementation if possible.
20 ///
21 /// ### Type Signature
22 ///
23 /// `forall a b f. (Traversable t, Applicative f) => (a -> f b, t a) -> f (t b)`
24 ///
25 /// ### Type Parameters
26 ///
27 /// * `F`: The applicative context.
28 /// * `Func`: The type of the function to apply.
29 /// * `A`: The type of the elements in the traversable structure.
30 /// * `B`: The type of the elements in the resulting traversable structure.
31 ///
32 /// ### Parameters
33 ///
34 /// * `func`: The function to apply to each element, returning a value in an applicative context.
35 /// * `ta`: The traversable structure.
36 ///
37 /// ### Returns
38 ///
39 /// The traversable structure wrapped in the applicative context.
40 ///
41 /// ### Examples
42 ///
43 /// ```
44 /// use fp_library::classes::traversable::Traversable;
45 /// use fp_library::brands::OptionBrand;
46 ///
47 /// let x = Some(5);
48 /// let y = OptionBrand::traverse::<OptionBrand, _, _, _>(|a| Some(a * 2), x);
49 /// assert_eq!(y, Some(Some(10)));
50 /// ```
51 fn traverse<'a, F: Applicative, Func, A: 'a + Clone, B: 'a + Clone>(
52 func: Func,
53 ta: Apply!(
54 brand: Self,
55 signature: ('a, A: 'a) -> 'a,
56 ),
57 ) -> Apply!(
58 brand: F,
59 signature: ('a, Apply!(brand: Self, signature: ('a, B: 'a) -> 'a): 'a) -> 'a,
60 )
61 where
62 Func: Fn(
63 A,
64 ) -> Apply!(
65 brand: F,
66 signature: ('a, B: 'a) -> 'a,
67 ) + 'a,
68 Apply!(
69 brand: Self,
70 signature: ('a, B: 'a) -> 'a,
71 ): Clone,
72 Apply!(
73 brand: F,
74 signature: ('a, B: 'a) -> 'a,
75 ): Clone,
76 {
77 Self::sequence::<F, B>(Self::map::<Func, A, _>(func, ta))
78 }
79
80 /// Evaluate each computation in a [`Traversable`] structure and accumulate the results into an [`Applicative`] context.
81 ///
82 /// The default implementation is defined in terms of [`traverse`] and [`identity`].
83 ///
84 /// ### Type Signature
85 ///
86 /// `forall a f. (Traversable t, Applicative f) => (t (f a)) -> f (t a)`
87 ///
88 /// ### Type Parameters
89 ///
90 /// * `F`: The applicative context.
91 /// * `A`: The type of the elements in the traversable structure.
92 ///
93 /// ### Parameters
94 ///
95 /// * `ta`: The traversable structure containing values in an applicative context.
96 ///
97 /// ### Returns
98 ///
99 /// The traversable structure wrapped in the applicative context.
100 ///
101 /// ### Examples
102 ///
103 /// ```
104 /// use fp_library::classes::traversable::Traversable;
105 /// use fp_library::brands::OptionBrand;
106 ///
107 /// let x = Some(Some(5));
108 /// let y = OptionBrand::sequence::<OptionBrand, _>(x);
109 /// assert_eq!(y, Some(Some(5)));
110 /// ```
111 fn sequence<'a, F: Applicative, A: 'a + Clone>(
112 ta: Apply!(
113 brand: Self,
114 signature: ('a, Apply!(brand: F, signature: ('a, A: 'a) -> 'a): 'a) -> 'a,
115 )
116 ) -> Apply!(
117 brand: F,
118 signature: ('a, Apply!(brand: Self, signature: ('a, A: 'a) -> 'a): 'a) -> 'a,
119 )
120 where
121 Apply!(
122 brand: F,
123 signature: ('a, A: 'a) -> 'a,
124 ): Clone,
125 Apply!(
126 brand: Self,
127 signature: ('a, A: 'a) -> 'a,
128 ): Clone,
129 {
130 Self::traverse::<
131 F,
132 _,
133 Apply!(
134 brand: F,
135 signature: ('a, A: 'a) -> 'a,
136 ),
137 A,
138 >(identity, ta)
139 }
140}
141
142/// Map each element of the [`Traversable`] structure to a computation, evaluate those computations and combine the results into an [`Applicative`] context.
143///
144/// Free function version that dispatches to [the type class' associated function][`Traversable::traverse`].
145///
146/// ### Type Signature
147///
148/// `forall a b f. (Traversable t, Applicative f) => (a -> f b, t a) -> f (t b)`
149///
150/// ### Type Parameters
151///
152/// * `Brand`: The brand of the traversable structure.
153/// * `F`: The applicative context.
154/// * `Func`: The type of the function to apply.
155/// * `A`: The type of the elements in the traversable structure.
156/// * `B`: The type of the elements in the resulting traversable structure.
157///
158/// ### Parameters
159///
160/// * `func`: The function to apply to each element, returning a value in an applicative context.
161/// * `ta`: The traversable structure.
162///
163/// ### Returns
164///
165/// The traversable structure wrapped in the applicative context.
166///
167/// ### Examples
168///
169/// ```
170/// use fp_library::classes::traversable::traverse;
171/// use fp_library::brands::OptionBrand;
172///
173/// let x = Some(5);
174/// let y = traverse::<OptionBrand, OptionBrand, _, _, _>(|a| Some(a * 2), x);
175/// assert_eq!(y, Some(Some(10)));
176/// ```
177pub fn traverse<'a, Brand: Traversable, F: Applicative, Func, A: 'a + Clone, B: 'a + Clone>(
178 func: Func,
179 ta: Apply!(
180 brand: Brand,
181 signature: ('a, A: 'a) -> 'a,
182 ),
183) -> Apply!(
184 brand: F,
185 signature: ('a, Apply!(brand: Brand, signature: ('a, B: 'a) -> 'a): 'a) -> 'a,
186)
187where
188 Func: Fn(
189 A,
190 ) -> Apply!(
191 brand: F,
192 signature: ('a, B: 'a) -> 'a,
193 ) + 'a,
194 Apply!(
195 brand: Brand,
196 signature: ('a, B: 'a) -> 'a,
197 ): Clone,
198 Apply!(
199 brand: F,
200 signature: ('a, B: 'a) -> 'a,
201 ): Clone,
202{
203 Brand::traverse::<F, Func, A, B>(func, ta)
204}
205
206/// Evaluate each computation in a [`Traversable`] structure and accumulate the results into an [`Applicative`] context.
207///
208/// Free function version that dispatches to [the type class' associated function][`Traversable::sequence`].
209///
210/// ### Type Signature
211///
212/// `forall a f. (Traversable t, Applicative f) => (t (f a)) -> f (t a)`
213///
214/// ### Type Parameters
215///
216/// * `Brand`: The brand of the traversable structure.
217/// * `F`: The applicative context.
218/// * `A`: The type of the elements in the traversable structure.
219///
220/// ### Parameters
221///
222/// * `ta`: The traversable structure containing values in an applicative context.
223///
224/// ### Returns
225///
226/// The traversable structure wrapped in the applicative context.
227///
228/// ### Examples
229///
230/// ```
231/// use fp_library::classes::traversable::sequence;
232/// use fp_library::brands::OptionBrand;
233///
234/// let x = Some(Some(5));
235/// let y = sequence::<OptionBrand, OptionBrand, _>(x);
236/// assert_eq!(y, Some(Some(5)));
237/// ```
238pub fn sequence<'a, Brand: Traversable, F: Applicative, A: 'a + Clone>(
239 ta: Apply!(
240 brand: Brand,
241 signature: ('a, Apply!(brand: F, signature: ('a, A: 'a) -> 'a): 'a) -> 'a,
242 )
243) -> Apply!(
244 brand: F,
245 signature: ('a, Apply!(brand: Brand, signature: ('a, A: 'a) -> 'a): 'a) -> 'a,
246)
247where
248 Apply!(
249 brand: F,
250 signature: ('a, A: 'a) -> 'a,
251 ): Clone,
252 Apply!(
253 brand: Brand,
254 signature: ('a, A: 'a) -> 'a,
255 ): Clone,
256{
257 Brand::sequence::<F, A>(ta)
258}