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