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