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}