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}