fp_library/classes/
witherable.rs

1//! Witherable type class.
2//!
3//! This module defines the [`Witherable`] trait, which represents data structures that can be traversed and filtered in an applicative context.
4
5use crate::{
6	Apply,
7	classes::{applicative::Applicative, filterable::Filterable, traversable::Traversable},
8	kinds::*,
9	types::Pair,
10};
11
12/// A type class for data structures that can be traversed and filtered.
13///
14/// `Witherable` extends [`Filterable`] and [`Traversable`], adding methods for:
15/// *   `wither`: Effectful `filter_map`.
16/// *   `wilt`: Effectful `partition_map`.
17pub trait Witherable: Filterable + Traversable {
18	/// Partitions a data structure based on a function that returns a `Result` in an applicative context.
19	///
20	/// The default implementation uses [`Traversable::traverse`] and [`Compactable::separate`](crate::classes::compactable::Compactable::separate).
21	///
22	/// ### Type Signature
23	///
24	/// `forall a e o m f. (Witherable f, Applicative m) => (a -> m (Result o e)) -> f a -> m (f o, f e)`
25	///
26	/// ### Type Parameters
27	///
28	/// * `Func`: The type of the function to apply.
29	/// * `M`: The applicative context.
30	/// * `A`: The type of the elements in the input structure.
31	/// * `E`: The type of the error values.
32	/// * `O`: The type of the success values.
33	///
34	/// ### Parameters
35	///
36	/// * `func`: The function to apply to each element, returning a `Result` in an applicative context.
37	/// * `ta`: The data structure to partition.
38	///
39	/// ### Returns
40	///
41	/// The partitioned data structure wrapped in the applicative context.
42	///
43	/// ### Examples
44	///
45	/// ```
46	/// use fp_library::classes::witherable::Witherable;
47	/// use fp_library::brands::OptionBrand;
48	/// use fp_library::types::Pair;
49	///
50	/// let x = Some(5);
51	/// let y = OptionBrand::wilt::<_, OptionBrand, _, _, _>(|a| Some(if a > 2 { Ok(a) } else { Err(a) }), x);
52	/// assert_eq!(y, Some(Pair(Some(5), None)));
53	/// ```
54	fn wilt<'a, Func, M: Applicative, A: 'a + Clone, E: 'a + Clone, O: 'a + Clone>(
55		func: Func,
56		ta: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
57	) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
58		'a,
59		Pair<
60			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
61			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
62		>,
63	>)
64	where
65		Func: Fn(A) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>) + 'a,
66		Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>): Clone,
67		Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>): Clone,
68	{
69		M::map(|res| Self::separate(res), Self::traverse::<M, Func, A, Result<O, E>>(func, ta))
70	}
71
72	/// Maps a function over a data structure and filters out `None` results in an applicative context.
73	///
74	/// The default implementation uses [`Traversable::traverse`] and [`Compactable::compact`](crate::classes::compactable::Compactable::compact).
75	///
76	/// ### Type Signature
77	///
78	/// `forall a b m f. (Witherable f, Applicative m) => (a -> m (Option b)) -> f a -> m (f b)`
79	///
80	/// ### Type Parameters
81	///
82	/// * `Func`: The type of the function to apply.
83	/// * `M`: The applicative context.
84	/// * `A`: The type of the elements in the input structure.
85	/// * `B`: The type of the elements in the output structure.
86	///
87	/// ### Parameters
88	///
89	/// * `func`: The function to apply to each element, returning an `Option` in an applicative context.
90	/// * `ta`: The data structure to filter and map.
91	///
92	/// ### Returns
93	///
94	/// The filtered and mapped data structure wrapped in the applicative context.
95	///
96	/// ### Examples
97	///
98	/// ```
99	/// use fp_library::classes::witherable::Witherable;
100	/// use fp_library::brands::OptionBrand;
101	///
102	/// let x = Some(5);
103	/// let y = OptionBrand::wither::<_, OptionBrand, _, _>(|a| Some(if a > 2 { Some(a * 2) } else { None }), x);
104	/// assert_eq!(y, Some(Some(10)));
105	/// ```
106	fn wither<'a, Func, M: Applicative, A: 'a + Clone, B: 'a + Clone>(
107		func: Func,
108		ta: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
109	) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
110		'a,
111		Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
112	>)
113	where
114		Func: Fn(A) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>) + 'a,
115		Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>): Clone,
116		Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>): Clone,
117	{
118		M::map(|opt| Self::compact(opt), Self::traverse::<M, Func, A, Option<B>>(func, ta))
119	}
120}
121
122/// Partitions a data structure based on a function that returns a `Result` in an applicative context.
123///
124/// Free function version that dispatches to [the type class' associated function][`Witherable::wilt`].
125///
126/// ### Type Signature
127///
128/// `forall a e o m f. (Witherable f, Applicative m) => (a -> m (Result o e)) -> f a -> m (f o, f e)`
129///
130/// ### Type Parameters
131///
132/// * `Brand`: The brand of the witherable structure.
133/// * `Func`: The type of the function to apply.
134/// * `M`: The applicative context.
135/// * `A`: The type of the elements in the input structure.
136/// * `E`: The type of the error values.
137/// * `O`: The type of the success values.
138///
139/// ### Parameters
140///
141/// * `func`: The function to apply to each element, returning a `Result` in an applicative context.
142/// * `ta`: The data structure to partition.
143///
144/// ### Returns
145///
146/// The partitioned data structure wrapped in the applicative context.
147///
148/// ### Examples
149///
150/// ```
151/// use fp_library::classes::witherable::wilt;
152/// use fp_library::brands::OptionBrand;
153/// use fp_library::types::Pair;
154///
155/// let x = Some(5);
156/// let y = wilt::<OptionBrand, _, OptionBrand, _, _, _>(|a| Some(if a > 2 { Ok(a) } else { Err(a) }), x);
157/// assert_eq!(y, Some(Pair(Some(5), None)));
158/// ```
159pub fn wilt<
160	'a,
161	Brand: Witherable,
162	Func,
163	M: Applicative,
164	A: 'a + Clone,
165	E: 'a + Clone,
166	O: 'a + Clone,
167>(
168	func: Func,
169	ta: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
170) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
171	'a,
172	Pair<
173		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
174		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
175	>,
176>)
177where
178	Func: Fn(A) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>) + 'a,
179	Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>): Clone,
180	Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>): Clone,
181{
182	Brand::wilt::<_, M, _, _, _>(func, ta)
183}
184
185/// Maps a function over a data structure and filters out `None` results in an applicative context.
186///
187/// Free function version that dispatches to [the type class' associated function][`Witherable::wither`].
188///
189/// ### Type Signature
190///
191/// `forall a b m f. (Witherable f, Applicative m) => (a -> m (Option b)) -> f a -> m (f b)`
192///
193/// ### Type Parameters
194///
195/// * `Brand`: The brand of the witherable structure.
196/// * `Func`: The type of the function to apply.
197/// * `M`: The applicative context.
198/// * `A`: The type of the elements in the input structure.
199/// * `B`: The type of the elements in the output structure.
200///
201/// ### Parameters
202///
203/// * `func`: The function to apply to each element, returning an `Option` in an applicative context.
204/// * `ta`: The data structure to filter and map.
205///
206/// ### Returns
207///
208/// The filtered and mapped data structure wrapped in the applicative context.
209///
210/// ### Examples
211///
212/// ```
213/// use fp_library::classes::witherable::wither;
214/// use fp_library::brands::OptionBrand;
215///
216/// let x = Some(5);
217/// let y = wither::<OptionBrand, _, OptionBrand, _, _>(|a| Some(if a > 2 { Some(a * 2) } else { None }), x);
218/// assert_eq!(y, Some(Some(10)));
219/// ```
220pub fn wither<'a, Brand: Witherable, Func, M: Applicative, A: 'a + Clone, B: 'a + Clone>(
221	func: Func,
222	ta: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
223) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
224	'a,
225	Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
226>)
227where
228	Func: Fn(A) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>) + 'a,
229	Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>): Clone,
230	Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>): Clone,
231{
232	Brand::wither::<_, M, _, _>(func, ta)
233}