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