Skip to main content

fp_library/classes/
compactable.rs

1//! Data structures that can be compacted by filtering out [`None`] or separated by splitting [`Result`] values.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! 	brands::*,
8//! 	functions::*,
9//! };
10//!
11//! let x = Some(Some(5));
12//! let y = compact::<OptionBrand, _>(x);
13//! assert_eq!(y, Some(5));
14//! ```
15
16#[fp_macros::document_module]
17mod inner {
18	use {
19		crate::{
20			brands::*,
21			kinds::*,
22		},
23		fp_macros::*,
24	};
25
26	/// A type class for data structures that can be compacted and separated.
27	///
28	/// `Compactable` allows for:
29	/// *   `compact`: Filtering out [`None`] values and unwrapping [`Some`] values from a structure of [`Option`]s.
30	/// *   `separate`: Splitting a structure of [`Result`]s into a pair of structures, one containing the [`Err`] values and the other containing the [`Ok`] values.
31	///
32	/// ### Laws
33	///
34	/// To be `Compactable` alone, no laws must be satisfied other than the type signature.
35	///
36	/// If the data type is also a [`Functor`](crate::classes::Functor):
37	/// * Identity: `compact(map(Some, fa)) = fa`.
38	///
39	/// If the data type is also [`Plus`](crate::classes::Plus):
40	/// * Annihilation (empty): `compact(empty) = empty`.
41	/// * Annihilation (map): `compact(map(|_| None, xs)) = empty`.
42	#[document_examples]
43	///
44	/// Compactable laws for [`Option`]:
45	///
46	/// ```
47	/// use fp_library::{
48	/// 	brands::*,
49	/// 	functions::*,
50	/// };
51	///
52	/// // Functor Identity: compact(map(Some, fa)) = fa
53	/// assert_eq!(compact::<OptionBrand, _>(map::<OptionBrand, _, _>(Some, Some(5))), Some(5),);
54	/// assert_eq!(compact::<OptionBrand, _>(map::<OptionBrand, _, _>(Some, None::<i32>)), None,);
55	///
56	/// // Plus Annihilation (empty): compact(empty) = empty
57	/// assert_eq!(
58	/// 	compact::<OptionBrand, _>(plus_empty::<OptionBrand, Option<i32>>()),
59	/// 	plus_empty::<OptionBrand, i32>(),
60	/// );
61	///
62	/// // Plus Annihilation (map): compact(map(|_| None, xs)) = empty
63	/// assert_eq!(
64	/// 	compact::<OptionBrand, _>(map::<OptionBrand, _, _>(|_: i32| None::<i32>, Some(5))),
65	/// 	plus_empty::<OptionBrand, i32>(),
66	/// );
67	/// ```
68	///
69	/// Compactable laws for [`Vec`]:
70	///
71	/// ```
72	/// use fp_library::{
73	/// 	brands::*,
74	/// 	functions::*,
75	/// };
76	///
77	/// // Functor Identity: compact(map(Some, fa)) = fa
78	/// assert_eq!(compact::<VecBrand, _>(map::<VecBrand, _, _>(Some, vec![1, 2, 3])), vec![1, 2, 3],);
79	///
80	/// // Plus Annihilation (empty): compact(empty) = empty
81	/// assert_eq!(
82	/// 	compact::<VecBrand, _>(plus_empty::<VecBrand, Option<i32>>()),
83	/// 	plus_empty::<VecBrand, i32>(),
84	/// );
85	///
86	/// // Plus Annihilation (map): compact(map(|_| None, xs)) = empty
87	/// assert_eq!(
88	/// 	compact::<VecBrand, _>(map::<VecBrand, _, _>(|_: i32| None::<i32>, vec![1, 2, 3])),
89	/// 	plus_empty::<VecBrand, i32>(),
90	/// );
91	/// ```
92	#[kind(type Of<'a, A: 'a>: 'a;)]
93	pub trait Compactable {
94		/// Compacts a data structure of [`Option`]s, discarding [`None`] values and keeping [`Some`] values.
95		#[document_signature]
96		///
97		#[document_type_parameters(
98			"The lifetime of the elements.",
99			"The type of the elements in the [`Option`]."
100		)]
101		///
102		#[document_parameters("The data structure containing [`Option`] values.")]
103		///
104		#[document_returns(
105			"A new data structure containing only the values from the [`Some`] variants."
106		)]
107		#[document_examples]
108		///
109		/// ```
110		/// use fp_library::{
111		/// 	brands::*,
112		/// 	functions::*,
113		/// };
114		///
115		/// let x = Some(Some(5));
116		/// let y = compact::<OptionBrand, _>(x);
117		/// assert_eq!(y, Some(5));
118		///
119		/// let z = Some(None::<i32>);
120		/// let w = compact::<OptionBrand, _>(z);
121		/// assert_eq!(w, None);
122		/// ```
123		fn compact<'a, A: 'a>(
124			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
125				'a,
126				Apply!(<OptionBrand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
127			>)
128		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>);
129
130		/// Separates a data structure of [`Result`]s into two data structures: one containing the [`Err`] values and one containing the [`Ok`] values.
131		#[document_signature]
132		///
133		#[document_type_parameters(
134			"The lifetime of the elements.",
135			"The type of the error values.",
136			"The type of the success values."
137		)]
138		///
139		#[document_parameters("The data structure containing [`Result`] values.")]
140		///
141		#[document_returns(
142			"A pair of data structures: the first containing the [`Err`] values, and the second containing the [`Ok`] values."
143		)]
144		#[document_examples]
145		///
146		/// ```
147		/// use fp_library::{
148		/// 	brands::*,
149		/// 	functions::*,
150		/// };
151		///
152		/// let x: Option<Result<i32, &str>> = Some(Ok(5));
153		/// let (errs, oks) = separate::<OptionBrand, _, _>(x);
154		/// assert_eq!(oks, Some(5));
155		/// assert_eq!(errs, None);
156		///
157		/// let y: Option<Result<i32, &str>> = Some(Err("error"));
158		/// let (errs2, oks2) = separate::<OptionBrand, _, _>(y);
159		/// assert_eq!(oks2, None);
160		/// assert_eq!(errs2, Some("error"));
161		/// ```
162		fn separate<'a, E: 'a, O: 'a>(
163			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>)
164		) -> (
165			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
166			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
167		);
168	}
169
170	/// Compacts a data structure of [`Option`]s, discarding [`None`] values and keeping [`Some`] values.
171	///
172	/// Free function version that dispatches to [the type class' associated function][`Compactable::compact`].
173	#[document_signature]
174	///
175	#[document_type_parameters(
176		"The lifetime of the elements.",
177		"The brand of the compactable structure.",
178		"The type of the elements in the [`Option`]."
179	)]
180	///
181	#[document_parameters("The data structure containing [`Option`] values.")]
182	///
183	#[document_returns(
184		"A new data structure containing only the values from the [`Some`] variants."
185	)]
186	#[document_examples]
187	///
188	/// ```
189	/// use fp_library::{
190	/// 	brands::*,
191	/// 	functions::*,
192	/// };
193	///
194	/// let x = Some(Some(5));
195	/// let y = compact::<OptionBrand, _>(x);
196	/// assert_eq!(y, Some(5));
197	/// ```
198	pub fn compact<'a, Brand: Compactable, A: 'a>(
199		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
200			'a,
201			Apply!(<OptionBrand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
202		>)
203	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
204		Brand::compact(fa)
205	}
206
207	/// Separates a data structure of [`Result`]s into two data structures: one containing the [`Err`] values and one containing the [`Ok`] values.
208	///
209	/// Free function version that dispatches to [the type class' associated function][`Compactable::separate`].
210	#[document_signature]
211	///
212	#[document_type_parameters(
213		"The lifetime of the elements.",
214		"The brand of the compactable structure.",
215		"The type of the error values.",
216		"The type of the success values."
217	)]
218	///
219	#[document_parameters("The data structure containing [`Result`] values.")]
220	///
221	#[document_returns(
222		"A pair of data structures: the first containing the [`Err`] values, and the second containing the [`Ok`] values."
223	)]
224	#[document_examples]
225	///
226	/// ```
227	/// use fp_library::{
228	/// 	brands::*,
229	/// 	functions::*,
230	/// };
231	///
232	/// let x: Option<Result<i32, &str>> = Some(Ok(5));
233	/// let (errs, oks) = separate::<OptionBrand, _, _>(x);
234	/// assert_eq!(oks, Some(5));
235	/// assert_eq!(errs, None);
236	/// ```
237	pub fn separate<'a, Brand: Compactable, E: 'a, O: 'a>(
238		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>)
239	) -> (
240		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
241		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
242	) {
243		Brand::separate::<E, O>(fa)
244	}
245}
246
247pub use inner::*;