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}