fp_library/typeclasses/
traversable.rs

1use std::sync::Arc;
2
3use crate::{
4	aliases::ArcFn,
5	functions::{identity, map},
6	hkt::Apply1,
7	typeclasses::{Applicative, Foldable, Functor},
8};
9
10/// A typeclass for traversable functors.
11///
12/// `Traversable` functors can be traversed, which accumulates results and effects in some [`Applicative`] context.
13///
14/// A minimum implementation of `Traversable` requires the manual implementation of at least [`Traversable::traverse`] or [`Traversable::sequence`].
15pub trait Traversable<'a>: Functor + Foldable {
16	/// Map each element of the [`Traversable`] structure to a computation, evaluate those computations and combine the results into an [`Applicative`] context.
17	///
18	/// The default implementation of `traverse` is implemented in terms of [`sequence`] and [`map`] where:
19	///
20	/// `(traverse f) ta = sequence ((map f) ta)`
21	///
22	/// # Type Signature
23	///
24	/// `forall t f a b. Traversable t, Applicative f => (a -> f b) -> t a -> f (t b)`
25	///
26	/// # Parameters
27	///
28	/// * `f`: A function that inputs the elements in the [`Traversable`] structure and outputs applicative computations for each.
29	/// * `ta`: A [`Traversable`] structure containing values.
30	///
31	/// # Returns
32	///
33	/// An [`Applicative`] containing the accumulated results of the computations.
34	///
35	/// # Examples
36	///
37	/// ```
38	/// use fp_library::{brands::{VecBrand, OptionBrand}, functions::traverse};
39	/// use std::sync::Arc;
40	///
41	/// assert_eq!(
42	///     traverse::<VecBrand, OptionBrand, i32, i32>(Arc::new(|x| Some(x * 2)))(vec![1, 2, 3]),
43	///     Some(vec![2, 4, 6])
44	/// );
45	/// ```
46	fn traverse<F: Applicative, A: 'a + Clone, B: 'a + Clone>(
47		f: ArcFn<'a, A, Apply1<F, B>>
48	) -> ArcFn<'a, Apply1<Self, A>, Apply1<F, Apply1<Self, B>>>
49	where
50		Apply1<F, B>: 'a + Clone,
51		Apply1<F, ArcFn<'a, Apply1<Self, B>, Apply1<Self, B>>>: Clone,
52	{
53		Arc::new(move |ta| Self::sequence::<F, B>(map::<Self, _, Apply1<F, B>>(f.clone())(ta)))
54	}
55
56	/// Evaluate each computation in a [`Traversable`] structure and accumulate the results into an [`Applicative`] context.
57	///
58	/// The default implementation of `sequence` is implemented in terms of [`traverse`] and [`identity`] where:
59	///
60	/// `sequence = traverse identity`
61	///
62	/// # Type Signature
63	///
64	/// `forall t f a. Traversable t, Applicative f => t (f a) -> f (t a)`
65	///
66	/// # Parameters
67	///
68	/// * `t`: A [`Traversable`] structure containing [`Applicative`] computations.
69	///
70	/// # Returns
71	///
72	/// An [`Applicative`] containing the accumulated results of the computations.
73	///
74	/// # Examples
75	///
76	/// ```
77	/// use fp_library::{brands::{VecBrand, OptionBrand}, functions::sequence};
78	/// use std::sync::Arc;
79	///
80	/// assert_eq!(
81	///     sequence::<VecBrand, OptionBrand, i32>(vec![Some(1), Some(2), Some(3)]),
82	///     Some(vec![1, 2, 3])
83	/// );
84	/// ```
85	fn sequence<F: Applicative, A: 'a + Clone>(
86		t: Apply1<Self, Apply1<F, A>>
87	) -> Apply1<F, Apply1<Self, A>>
88	where
89		Apply1<F, A>: 'a + Clone,
90		Apply1<F, ArcFn<'a, Apply1<Self, A>, Apply1<Self, A>>>: Clone,
91	{
92		(Self::traverse::<F, _, A>(Arc::new(identity)))(t)
93	}
94}
95
96/// Map each element of the [`Traversable`] structure to a computation, evaluate those computations and combine the results into an [`Applicative`] context.
97///
98/// Free function version that dispatches to [the typeclass' associated function][`Traversable::traverse`].
99///
100/// The default implementation of `traverse` is implemented in terms of [`sequence`] and [`map`] where:
101///
102/// `(traverse f) ta = sequence ((map f) ta)`
103///
104/// # Type Signature
105///
106/// `forall t f a b. Traversable t, Applicative f => (a -> f b) -> t a -> f (t b)`
107///
108/// # Parameters
109///
110/// * `f`: A function that inputs the elements in the [`Traversable`] structure and outputs applicative computations for each.
111/// * `ta`: A [`Traversable`] structure containing values.
112///
113/// # Returns
114///
115/// An [`Applicative`] containing the accumulated results of the computations.
116///
117/// # Examples
118///
119/// ```
120/// use fp_library::{brands::{VecBrand, OptionBrand}, functions::traverse};
121/// use std::sync::Arc;
122///
123/// assert_eq!(
124///     traverse::<VecBrand, OptionBrand, i32, i32>(Arc::new(|x| Some(x * 2)))(vec![1, 2, 3]),
125///     Some(vec![2, 4, 6])
126/// );
127/// ```
128pub fn traverse<'a, Brand: Traversable<'a>, F: Applicative, A: 'a + Clone, B: 'a + Clone>(
129	f: ArcFn<'a, A, Apply1<F, B>>
130) -> ArcFn<'a, Apply1<Brand, A>, Apply1<F, Apply1<Brand, B>>>
131where
132	Apply1<F, B>: 'a + Clone,
133	Apply1<F, ArcFn<'a, Apply1<Brand, B>, Apply1<Brand, B>>>: Clone,
134{
135	Brand::traverse::<F, _, B>(f)
136}
137
138/// Evaluate each computation in a [`Traversable`] structure and accumulate the results into an [`Applicative`] context.
139///
140/// Free function version that dispatches to [the typeclass' associated function][`Traversable::sequence`].
141///
142/// The default implementation of `sequence` is implemented in terms of [`traverse`] and [`identity`] where:
143///
144/// `sequence = traverse identity`
145///
146/// # Type Signature
147///
148/// `forall t f a. Traversable t, Applicative f => t (f a) -> f (t a)`
149///
150/// # Parameters
151///
152/// * `t`: A [`Traversable`] structure containing [`Applicative`] computations.
153///
154/// # Returns
155///
156/// An [`Applicative`] containing the accumulated results of the computations.
157///
158/// # Examples
159///
160/// ```
161/// use fp_library::{brands::{VecBrand, OptionBrand}, functions::sequence};
162/// use std::sync::Arc;
163///
164/// assert_eq!(
165///     sequence::<VecBrand, OptionBrand, i32>(vec![Some(1), Some(2), Some(3)]),
166///     Some(vec![1, 2, 3])
167/// );
168/// ```
169pub fn sequence<'a, Brand: Traversable<'a>, F: Applicative, A: 'a + Clone>(
170	t: Apply1<Brand, Apply1<F, A>>
171) -> Apply1<F, Apply1<Brand, A>>
172where
173	Apply1<F, A>: 'a + Clone,
174	Apply1<F, ArcFn<'a, Apply1<Brand, A>, Apply1<Brand, A>>>: Clone,
175{
176	Brand::sequence::<F, A>(t)
177}