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