Skip to main content

fp_library/classes/
filterable.rs

1//! Data structures that can be filtered and partitioned based on predicates or mapping functions.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! 	brands::*,
8//! 	functions::*,
9//! };
10//!
11//! let x = Some(5);
12//! let y = filter::<OptionBrand, _>(|a| a > 2, x);
13//! assert_eq!(y, Some(5));
14//! ```
15
16#[fp_macros::document_module]
17mod inner {
18	use {
19		crate::{
20			classes::*,
21			kinds::*,
22		},
23		fp_macros::*,
24	};
25
26	/// A type class for data structures that can be filtered and partitioned.
27	///
28	/// `Filterable` extends [`Compactable`] and [`Functor`], adding methods for:
29	/// *   `filter`: Keeping elements that satisfy a predicate.
30	/// *   `filter_map`: Mapping and filtering in one step.
31	/// *   `partition`: Splitting elements based on a predicate.
32	/// *   `partition_map`: Mapping and partitioning in one step.
33	///
34	/// ### Minimal Implementation
35	///
36	/// A minimal implementation of `Filterable` requires no specific method implementations, as all methods have default implementations based on [`Compactable`] and [`Functor`].
37	///
38	/// However, it is recommended to implement [`Filterable::partition_map`] and [`Filterable::filter_map`] to avoid the intermediate structure created by the default implementations (which use [`map`](crate::functions::map) followed by [`separate`](crate::functions::separate) or [`compact`](crate::functions::compact)).
39	///
40	/// *   If [`Filterable::partition_map`] is implemented, [`Filterable::partition`] is derived from it.
41	/// *   If [`Filterable::filter_map`] is implemented, [`Filterable::filter`] is derived from it.
42	pub trait Filterable: Compactable + Functor {
43		/// Partitions a data structure based on a function that returns a [`Result`].
44		///
45		/// The default implementation uses [`map`](crate::functions::map) and [`separate`](crate::functions::separate).
46		#[document_signature]
47		///
48		#[document_type_parameters(
49			"The lifetime of the elements.",
50			"The type of the elements in the input structure.",
51			"The type of the error values.",
52			"The type of the success values."
53		)]
54		///
55		#[document_parameters(
56			"The function to apply to each element, returning a [`Result`].",
57			"The data structure to partition."
58		)]
59		///
60		#[document_returns(
61			"A pair of data structures: the first containing the [`Err`] values, and the second containing the [`Ok`] values."
62		)]
63		#[document_examples]
64		///
65		/// ```
66		/// use fp_library::{
67		/// 	brands::*,
68		/// 	functions::*,
69		/// };
70		///
71		/// let x = Some(5);
72		/// let (errs, oks) =
73		/// 	partition_map::<OptionBrand, _, _, _>(|a| if a > 2 { Ok(a) } else { Err(a) }, x);
74		/// assert_eq!(oks, Some(5));
75		/// assert_eq!(errs, None);
76		/// ```
77		fn partition_map<'a, A: 'a, E: 'a, O: 'a>(
78			func: impl Fn(A) -> Result<O, E> + 'a,
79			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
80		) -> (
81			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
82			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
83		) {
84			Self::separate::<E, O>(Self::map::<A, Result<O, E>>(func, fa))
85		}
86
87		/// Partitions a data structure based on a predicate.
88		///
89		/// The default implementation uses [`partition_map`].
90		///
91		/// **Note**: The return order is `(satisfied, not_satisfied)`, matching Rust's [`Iterator::partition`].
92		/// This is achieved by mapping satisfied elements to [`Ok`] and unsatisfied elements to [`Err`] internally,
93		/// as `separate` returns `(Oks, Errs)`.
94		#[document_signature]
95		///
96		#[document_type_parameters(
97			"The lifetime of the elements.",
98			"The type of the elements in the structure."
99		)]
100		///
101		#[document_parameters("The predicate function.", "The data structure to partition.")]
102		///
103		#[document_returns(
104			"A pair of data structures: the first containing elements that do not satisfy the predicate, and the second containing elements that do."
105		)]
106		#[document_examples]
107		///
108		/// ```
109		/// use fp_library::{
110		/// 	brands::*,
111		/// 	functions::*,
112		/// };
113		///
114		/// let x = Some(5);
115		/// let (not_satisfied, satisfied) = partition::<OptionBrand, _>(|a| a > 2, x);
116		/// assert_eq!(satisfied, Some(5));
117		/// assert_eq!(not_satisfied, None);
118		/// ```
119		fn partition<'a, A: 'a + Clone>(
120			func: impl Fn(A) -> bool + 'a,
121			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
122		) -> (
123			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
124			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
125		) {
126			Self::partition_map(move |a| if func(a.clone()) { Ok(a) } else { Err(a) }, fa)
127		}
128
129		/// Maps a function over a data structure and filters out [`None`] results.
130		///
131		/// The default implementation uses [`map`](crate::functions::map) and [`compact`](crate::functions::compact).
132		///
133		/// ### Type Signature
134		#[document_signature]
135		///
136		#[document_type_parameters(
137			"The lifetime of the elements.",
138			"The type of the elements in the input structure.",
139			"The type of the elements in the output structure."
140		)]
141		#[document_parameters(
142			"The function to apply to each element, returning an [`Option`].",
143			"The data structure to filter and map."
144		)]
145		#[document_returns(
146			"A new data structure containing only the values where the function returned [`Some`]."
147		)]
148		#[document_examples]
149		///
150		/// ```
151		/// use fp_library::{
152		/// 	brands::*,
153		/// 	functions::*,
154		/// };
155		///
156		/// let x = Some(5);
157		/// let y = filter_map::<OptionBrand, _, _>(|a| if a > 2 { Some(a * 2) } else { None }, x);
158		/// assert_eq!(y, Some(10));
159		/// ```
160		fn filter_map<'a, A: 'a, B: 'a>(
161			func: impl Fn(A) -> Option<B> + 'a,
162			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
163		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
164			Self::compact::<B>(Self::map::<A, Option<B>>(func, fa))
165		}
166
167		/// Filters a data structure based on a predicate.
168		///
169		/// The default implementation uses [`filter_map`].
170		#[document_signature]
171		///
172		#[document_type_parameters(
173			"The lifetime of the elements.",
174			"The type of the elements in the structure."
175		)]
176		///
177		#[document_parameters("The predicate function.", "The data structure to filter.")]
178		///
179		#[document_returns(
180			"A new data structure containing only the elements that satisfy the predicate."
181		)]
182		#[document_examples]
183		///
184		/// ```
185		/// use fp_library::{
186		/// 	brands::*,
187		/// 	functions::*,
188		/// };
189		///
190		/// let x = Some(5);
191		/// let y = filter::<OptionBrand, _>(|a| a > 2, x);
192		/// assert_eq!(y, Some(5));
193		/// ```
194		fn filter<'a, A: 'a + Clone>(
195			func: impl Fn(A) -> bool + 'a,
196			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
197		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
198			Self::filter_map(move |a| if func(a.clone()) { Some(a) } else { None }, fa)
199		}
200	}
201
202	/// Partitions a data structure based on a function that returns a [`Result`].
203	///
204	/// Free function version that dispatches to [the type class' associated function][`Filterable::partition_map`].
205	#[document_signature]
206	///
207	#[document_type_parameters(
208		"The lifetime of the elements.",
209		"The brand of the filterable structure.",
210		"The type of the elements in the input structure.",
211		"The type of the error values.",
212		"The type of the success values."
213	)]
214	///
215	#[document_parameters(
216		"The function to apply to each element, returning a [`Result`].",
217		"The data structure to partition."
218	)]
219	///
220	#[document_returns(
221		"A pair of data structures: the first containing the [`Err`] values, and the second containing the [`Ok`] values."
222	)]
223	#[document_examples]
224	///
225	/// ```
226	/// use fp_library::{
227	/// 	brands::*,
228	/// 	functions::*,
229	/// };
230	///
231	/// let x = Some(5);
232	/// let (errs, oks) =
233	/// 	partition_map::<OptionBrand, _, _, _>(|a| if a > 2 { Ok(a) } else { Err(a) }, x);
234	/// assert_eq!(oks, Some(5));
235	/// assert_eq!(errs, None);
236	/// ```
237	pub fn partition_map<'a, Brand: Filterable, A: 'a, E: 'a, O: 'a>(
238		func: impl Fn(A) -> Result<O, E> + 'a,
239		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
240	) -> (
241		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
242		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
243	) {
244		Brand::partition_map(func, fa)
245	}
246
247	/// Partitions a data structure based on a predicate.
248	///
249	/// Free function version that dispatches to [the type class' associated function][`Filterable::partition`].
250	///
251	/// **Note**: The return order is `(satisfied, not_satisfied)`, matching Rust's [`Iterator::partition`].
252	#[document_signature]
253	///
254	#[document_type_parameters(
255		"The lifetime of the elements.",
256		"The brand of the filterable structure.",
257		"The type of the elements in the structure."
258	)]
259	///
260	#[document_parameters("The predicate function.", "The data structure to partition.")]
261	///
262	#[document_returns(
263		"A pair of data structures: the first containing elements that do not satisfy the predicate, and the second containing elements that do."
264	)]
265	#[document_examples]
266	///
267	/// ```
268	/// use fp_library::{
269	/// 	brands::*,
270	/// 	functions::*,
271	/// };
272	///
273	/// let x = Some(5);
274	/// let (not_satisfied, satisfied) = partition::<OptionBrand, _>(|a| a > 2, x);
275	/// assert_eq!(satisfied, Some(5));
276	/// assert_eq!(not_satisfied, None);
277	/// ```
278	pub fn partition<'a, Brand: Filterable, A: 'a + Clone>(
279		func: impl Fn(A) -> bool + 'a,
280		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
281	) -> (
282		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
283		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
284	) {
285		Brand::partition(func, fa)
286	}
287
288	/// Maps a function over a data structure and filters out [`None`] results.
289	///
290	/// Free function version that dispatches to [the type class' associated function][`Filterable::filter_map`].
291	#[document_signature]
292	///
293	#[document_type_parameters(
294		"The lifetime of the elements.",
295		"The brand of the filterable structure.",
296		"The type of the elements in the input structure.",
297		"The type of the elements in the output structure."
298	)]
299	///
300	#[document_parameters(
301		"The function to apply to each element, returning an [`Option`].",
302		"The data structure to filter and map."
303	)]
304	///
305	#[document_returns(
306		"A new data structure containing only the values where the function returned [`Some`]."
307	)]
308	#[document_examples]
309	///
310	/// ```
311	/// use fp_library::{
312	/// 	brands::*,
313	/// 	functions::*,
314	/// };
315	///
316	/// let x = Some(5);
317	/// let y = filter_map::<OptionBrand, _, _>(|a| if a > 2 { Some(a * 2) } else { None }, x);
318	/// assert_eq!(y, Some(10));
319	/// ```
320	pub fn filter_map<'a, Brand: Filterable, A: 'a, B: 'a>(
321		func: impl Fn(A) -> Option<B> + 'a,
322		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
323	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
324		Brand::filter_map(func, fa)
325	}
326
327	/// Filters a data structure based on a predicate.
328	///
329	/// Free function version that dispatches to [the type class' associated function][`Filterable::filter`].
330	#[document_signature]
331	///
332	#[document_type_parameters(
333		"The lifetime of the elements.",
334		"The brand of the filterable structure.",
335		"The type of the elements in the structure."
336	)]
337	///
338	#[document_parameters("The predicate function.", "The data structure to filter.")]
339	///
340	#[document_returns(
341		"A new data structure containing only the elements that satisfy the predicate."
342	)]
343	#[document_examples]
344	///
345	/// ```
346	/// use fp_library::{
347	/// 	brands::*,
348	/// 	functions::*,
349	/// };
350	///
351	/// let x = Some(5);
352	/// let y = filter::<OptionBrand, _>(|a| a > 2, x);
353	/// assert_eq!(y, Some(5));
354	/// ```
355	pub fn filter<'a, Brand: Filterable, A: 'a + Clone>(
356		func: impl Fn(A) -> bool + 'a,
357		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
358	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
359		Brand::filter(func, fa)
360	}
361}
362
363pub use inner::*;