fp_library/classes/
traversable.rs

1use super::{applicative::Applicative, foldable::Foldable, functor::Functor};
2use crate::{functions::identity, hkt::Apply1L1T};
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: Apply1L1T<'a, Self, A>,
43	) -> Apply1L1T<'a, F, Apply1L1T<'a, Self, B>>
44	where
45		Func: Fn(A) -> Apply1L1T<'a, F, B> + 'a,
46		Apply1L1T<'a, Self, B>: Clone,
47		Apply1L1T<'a, F, B>: Clone,
48	{
49		Self::sequence::<F, B>(Self::map(f, ta))
50	}
51
52	/// Evaluate each computation in a [`Traversable`] structure and accumulate the results into an [`Applicative`] context.
53	///
54	/// The default implementation is defined in terms of [`traverse`] and [`identity`].
55	///
56	/// # Type Signature
57	///
58	/// `forall a f. (Traversable t, Applicative f) => (t (f a)) -> f (t a)`
59	///
60	/// # Parameters
61	///
62	/// * `ta`: The traversable structure containing values in an applicative context.
63	///
64	/// # Returns
65	///
66	/// The traversable structure wrapped in the applicative context.
67	///
68	/// # Examples
69	///
70	/// ```
71	/// use fp_library::classes::traversable::Traversable;
72	/// use fp_library::brands::OptionBrand;
73	///
74	/// let x = Some(Some(5));
75	/// let y = OptionBrand::sequence::<OptionBrand, _>(x);
76	/// assert_eq!(y, Some(Some(5)));
77	/// ```
78	fn sequence<'a, F: Applicative, A: 'a + Clone>(
79		ta: Apply1L1T<'a, Self, Apply1L1T<'a, F, A>>
80	) -> Apply1L1T<'a, F, Apply1L1T<'a, Self, A>>
81	where
82		Apply1L1T<'a, F, A>: Clone,
83		Apply1L1T<'a, Self, A>: Clone,
84	{
85		Self::traverse::<F, Apply1L1T<'a, F, A>, A, _>(identity, ta)
86	}
87}
88
89/// Map each element of the [`Traversable`] structure to a computation, evaluate those computations and combine the results into an [`Applicative`] context.
90///
91/// Free function version that dispatches to [the type class' associated function][`Traversable::traverse`].
92///
93/// # Type Signature
94///
95/// `forall a b f. (Traversable t, Applicative f) => (a -> f b, t a) -> f (t b)`
96///
97/// # Parameters
98///
99/// * `f`: The function to apply to each element, returning a value in an applicative context.
100/// * `ta`: The traversable structure.
101///
102/// # Returns
103///
104/// The traversable structure wrapped in the applicative context.
105///
106/// # Examples
107///
108/// ```
109/// use fp_library::classes::traversable::traverse;
110/// use fp_library::brands::OptionBrand;
111///
112/// let x = Some(5);
113/// let y = traverse::<OptionBrand, OptionBrand, _, _, _>(|a| Some(a * 2), x);
114/// assert_eq!(y, Some(Some(10)));
115/// ```
116pub fn traverse<'a, Brand: Traversable, F: Applicative, A: 'a + Clone, B: 'a + Clone, Func>(
117	f: Func,
118	ta: Apply1L1T<'a, Brand, A>,
119) -> Apply1L1T<'a, F, Apply1L1T<'a, Brand, B>>
120where
121	Func: Fn(A) -> Apply1L1T<'a, F, B> + 'a,
122	Apply1L1T<'a, Brand, B>: Clone,
123	Apply1L1T<'a, F, B>: Clone,
124{
125	Brand::traverse::<F, A, B, Func>(f, ta)
126}
127
128/// Evaluate each computation in a [`Traversable`] structure and accumulate the results into an [`Applicative`] context.
129///
130/// Free function version that dispatches to [the type class' associated function][`Traversable::sequence`].
131///
132/// # Type Signature
133///
134/// `forall a f. (Traversable t, Applicative f) => (t (f a)) -> f (t a)`
135///
136/// # Parameters
137///
138/// * `ta`: The traversable structure containing values in an applicative context.
139///
140/// # Returns
141///
142/// The traversable structure wrapped in the applicative context.
143///
144/// # Examples
145///
146/// ```
147/// use fp_library::classes::traversable::sequence;
148/// use fp_library::brands::OptionBrand;
149///
150/// let x = Some(Some(5));
151/// let y = sequence::<OptionBrand, OptionBrand, _>(x);
152/// assert_eq!(y, Some(Some(5)));
153/// ```
154pub fn sequence<'a, Brand: Traversable, F: Applicative, A: 'a + Clone>(
155	ta: Apply1L1T<'a, Brand, Apply1L1T<'a, F, A>>
156) -> Apply1L1T<'a, F, Apply1L1T<'a, Brand, A>>
157where
158	Apply1L1T<'a, F, A>: Clone,
159	Apply1L1T<'a, Brand, A>: Clone,
160{
161	Brand::sequence::<F, A>(ta)
162}