Skip to main content

fp_library/classes/
witherable.rs

1//! Data structures that can be traversed and filtered simultaneously in an applicative context.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{functions::*, brands::*};
7//!
8//! let x = Some(5);
9//! let y = wither::<OptionBrand, OptionBrand, _, _, _>(|a| Some(if a > 2 { Some(a * 2) } else { None }), x);
10//! assert_eq!(y, Some(Some(10)));
11//! ```
12
13use crate::{
14	Apply,
15	classes::{Applicative, Filterable, Traversable},
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 traversed and filtered.
24///
25/// `Witherable` extends [`Filterable`] and [`Traversable`], adding methods for:
26/// *   `wither`: Effectful `filter_map`.
27/// *   `wilt`: Effectful `partition_map`.
28///
29/// ### Minimal Implementation
30///
31/// A minimal implementation of `Witherable` requires no specific method implementations, as all methods have default implementations based on [`Traversable`] and [`Compactable`](crate::classes::compactable::Compactable).
32///
33/// However, it is recommended to implement [`Witherable::wilt`] and [`Witherable::wither`] to avoid the intermediate structure created by the default implementations (which use [`traverse`](crate::functions::traverse) followed by [`separate`](crate::functions::separate) or [`compact`](crate::functions::compact)).
34pub trait Witherable: Filterable + Traversable {
35	/// Partitions a data structure based on a function that returns a [`Result`] in an applicative context.
36	///
37	/// The default implementation uses [`traverse`](crate::functions::traverse) and [`separate`](crate::functions::separate).
38	///
39	/// ### Type Signature
40	///
41	#[hm_signature(Witherable)]
42	///
43	/// ### Type Parameters
44	///
45	#[doc_type_params(
46		"The lifetime of the elements.",
47		"The applicative context.",
48		"The type of the elements in the input structure.",
49		"The type of the success values.",
50		"The type of the error values.",
51		"The type of the function to apply."
52	)]
53	///
54	/// ### Parameters
55	///
56	#[doc_params(
57		"The function to apply to each element, returning a [`Result`] in an applicative context.",
58		"The data structure to partition."
59	)]
60	///
61	/// ### Returns
62	///
63	/// The partitioned data structure wrapped in the applicative context.
64	///
65	/// ### Examples
66	///
67	/// ```
68	/// use fp_library::{functions::*, brands::*, types::*};
69	///
70	/// let x = Some(5);
71	/// let y = wilt::<OptionBrand, OptionBrand, _, _, _, _>(|a| Some(if a > 2 { Ok(a) } else { Err(a) }), x);
72	/// assert_eq!(y, Some(Pair(Some(5), None)));
73	/// ```
74	fn wilt<'a, M: Applicative, A: 'a + Clone, O: 'a + Clone, E: 'a + Clone, Func>(
75		func: Func,
76		ta: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
77	) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
78		'a,
79		Pair<
80			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
81			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
82		>,
83	>)
84	where
85		Func: Fn(A) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>) + 'a,
86		Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>): Clone,
87		Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>): Clone,
88	{
89		M::map(|res| Self::separate(res), Self::traverse::<A, Result<O, E>, M, Func>(func, ta))
90	}
91
92	/// Maps a function over a data structure and filters out [`None`] results in an applicative context.
93	///
94	/// The default implementation uses [`traverse`](crate::functions::traverse) and [`compact`](crate::functions::compact).
95	///
96	/// ### Type Signature
97	///
98	#[hm_signature(Witherable)]
99	///
100	/// ### Type Parameters
101	///
102	#[doc_type_params(
103		"The lifetime of the elements.",
104		"The applicative context.",
105		"The type of the elements in the input structure.",
106		"The type of the elements in the output structure.",
107		"The type of the function to apply."
108	)]
109	///
110	/// ### Parameters
111	///
112	#[doc_params(
113		"The function to apply to each element, returning an [`Option`] in an applicative context.",
114		"The data structure to filter and map."
115	)]
116	///
117	/// ### Returns
118	///
119	/// The filtered and mapped data structure wrapped in the applicative context.
120	///
121	/// ### Examples
122	///
123	/// ```
124	/// use fp_library::{functions::*, brands::*};
125	///
126	/// let x = Some(5);
127	/// let y = wither::<OptionBrand, OptionBrand, _, _, _>(|a| Some(if a > 2 { Some(a * 2) } else { None }), x);
128	/// assert_eq!(y, Some(Some(10)));
129	/// ```
130	fn wither<'a, M: Applicative, A: 'a + Clone, B: 'a + Clone, Func>(
131		func: Func,
132		ta: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
133	) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
134		'a,
135		Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
136	>)
137	where
138		Func: Fn(A) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>) + 'a,
139		Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>): Clone,
140		Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>): Clone,
141	{
142		M::map(|opt| Self::compact(opt), Self::traverse::<A, Option<B>, M, Func>(func, ta))
143	}
144}
145
146/// Partitions a data structure based on a function that returns a [`Result`] in an applicative context.
147///
148/// Free function version that dispatches to [the type class' associated function][`Witherable::wilt`].
149///
150/// ### Type Signature
151///
152#[hm_signature(Witherable)]
153///
154/// ### Type Parameters
155///
156#[doc_type_params(
157	"The lifetime of the elements.",
158	"The brand of the witherable structure.",
159	"The applicative context.",
160	"The type of the elements in the input structure.",
161	"The type of the success values.",
162	"The type of the error values.",
163	"The type of the function to apply."
164)]
165///
166/// ### Parameters
167///
168#[doc_params(
169	"The function to apply to each element, returning a [`Result`] in an applicative context.",
170	"The data structure to partition."
171)]
172///
173/// ### Returns
174///
175/// The partitioned data structure wrapped in the applicative context.
176///
177/// ### Examples
178///
179/// ```
180/// use fp_library::{functions::*, brands::*, types::*};
181///
182/// let x = Some(5);
183/// let y = wilt::<OptionBrand, OptionBrand, _, _, _, _>(|a| Some(if a > 2 { Ok(a) } else { Err(a) }), x);
184/// assert_eq!(y, Some(Pair(Some(5), None)));
185/// ```
186pub fn wilt<'a, F: Witherable, M: Applicative, A: 'a + Clone, O: 'a + Clone, E: 'a + Clone, Func>(
187	func: Func,
188	ta: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
189) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
190	'a,
191	Pair<
192		Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
193		Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
194	>,
195>)
196where
197	Func: Fn(A) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>) + 'a,
198	Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>): Clone,
199	Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>): Clone,
200{
201	F::wilt::<M, A, O, E, Func>(func, ta)
202}
203
204/// Maps a function over a data structure and filters out [`None`] results in an applicative context.
205///
206/// Free function version that dispatches to [the type class' associated function][`Witherable::wither`].
207///
208/// ### Type Signature
209///
210#[hm_signature(Witherable)]
211///
212/// ### Type Parameters
213///
214#[doc_type_params(
215	"The lifetime of the elements.",
216	"The brand of the witherable structure.",
217	"The applicative context.",
218	"The type of the elements in the input structure.",
219	"The type of the elements in the output structure.",
220	"The type of the function to apply."
221)]
222///
223/// ### Parameters
224///
225#[doc_params(
226	"The function to apply to each element, returning an [`Option`] in an applicative context.",
227	"The data structure to filter and map."
228)]
229///
230/// ### Returns
231///
232/// The filtered and mapped data structure wrapped in the applicative context.
233///
234/// ### Examples
235///
236/// ```
237/// use fp_library::{functions::*, brands::*};
238///
239/// let x = Some(5);
240/// let y = wither::<OptionBrand, OptionBrand, _, _, _>(|a| Some(if a > 2 { Some(a * 2) } else { None }), x);
241/// assert_eq!(y, Some(Some(10)));
242/// ```
243pub fn wither<'a, F: Witherable, M: Applicative, A: 'a + Clone, B: 'a + Clone, Func>(
244	func: Func,
245	ta: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
246) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
247	'a,
248	Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
249>)
250where
251	Func: Fn(A) -> Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>) + 'a,
252	Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>): Clone,
253	Apply!(<M as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Option<B>>): Clone,
254{
255	F::wither::<M, A, B, Func>(func, ta)
256}