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}