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::explicit::*,
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	/// 		explicit::{
51	/// 			compact,
52	/// 			map,
53	/// 		},
54	/// 		*,
55	/// 	},
56	/// };
57	///
58	/// // Functor Identity: compact(map(Some, fa)) = fa
59	/// assert_eq!(
60	/// 	compact::<OptionBrand, _, _, _>(map::<OptionBrand, _, _, _, _>(Some, Some(5))),
61	/// 	Some(5),
62	/// );
63	/// assert_eq!(
64	/// 	compact::<OptionBrand, _, _, _>(map::<OptionBrand, _, _, _, _>(Some, None::<i32>)),
65	/// 	None,
66	/// );
67	///
68	/// // Plus Annihilation (empty): compact(empty) = empty
69	/// assert_eq!(
70	/// 	compact::<OptionBrand, _, _, _>(plus_empty::<OptionBrand, Option<i32>>()),
71	/// 	plus_empty::<OptionBrand, i32>(),
72	/// );
73	///
74	/// // Plus Annihilation (map): compact(map(|_| None, xs)) = empty
75	/// assert_eq!(
76	/// 	compact::<OptionBrand, _, _, _>(map::<OptionBrand, _, _, _, _>(
77	/// 		|_: i32| None::<i32>,
78	/// 		Some(5)
79	/// 	)),
80	/// 	plus_empty::<OptionBrand, i32>(),
81	/// );
82	/// ```
83	///
84	/// Compactable laws for [`Vec`]:
85	///
86	/// ```
87	/// use fp_library::{
88	/// 	brands::*,
89	/// 	functions::{
90	/// 		explicit::{
91	/// 			compact,
92	/// 			map,
93	/// 		},
94	/// 		*,
95	/// 	},
96	/// };
97	///
98	/// // Functor Identity: compact(map(Some, fa)) = fa
99	/// assert_eq!(
100	/// 	compact::<VecBrand, _, _, _>(map::<VecBrand, _, _, _, _>(Some, vec![1, 2, 3])),
101	/// 	vec![1, 2, 3],
102	/// );
103	///
104	/// // Plus Annihilation (empty): compact(empty) = empty
105	/// assert_eq!(
106	/// 	compact::<VecBrand, _, _, _>(plus_empty::<VecBrand, Option<i32>>()),
107	/// 	plus_empty::<VecBrand, i32>(),
108	/// );
109	///
110	/// // Plus Annihilation (map): compact(map(|_| None, xs)) = empty
111	/// assert_eq!(
112	/// 	compact::<VecBrand, _, _, _>(map::<VecBrand, _, _, _, _>(
113	/// 		|_: i32| None::<i32>,
114	/// 		vec![1, 2, 3]
115	/// 	)),
116	/// 	plus_empty::<VecBrand, i32>(),
117	/// );
118	/// ```
119	#[kind(type Of<'a, A: 'a>: 'a;)]
120	pub trait Compactable {
121		/// Compacts a data structure of [`Option`]s, discarding [`None`] values and keeping [`Some`] values.
122		#[document_signature]
123		///
124		#[document_type_parameters(
125			"The lifetime of the elements.",
126			"The type of the elements in the [`Option`]."
127		)]
128		///
129		#[document_parameters("The data structure containing [`Option`] values.")]
130		///
131		#[document_returns(
132			"A new data structure containing only the values from the [`Some`] variants."
133		)]
134		#[document_examples]
135		///
136		/// ```
137		/// use fp_library::{
138		/// 	brands::*,
139		/// 	functions::explicit::*,
140		/// };
141		///
142		/// let x = Some(Some(5));
143		/// let y = compact::<OptionBrand, _, _, _>(x);
144		/// assert_eq!(y, Some(5));
145		///
146		/// let z = Some(None::<i32>);
147		/// let w = compact::<OptionBrand, _, _, _>(z);
148		/// assert_eq!(w, None);
149		/// ```
150		fn compact<'a, A: 'a>(
151			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
152				'a,
153				Apply!(<OptionBrand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
154			>)
155		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>);
156
157		/// Separates a data structure of [`Result`]s into two data structures: one containing the [`Err`] values and one containing the [`Ok`] values.
158		#[document_signature]
159		///
160		#[document_type_parameters(
161			"The lifetime of the elements.",
162			"The type of the error values.",
163			"The type of the success values."
164		)]
165		///
166		#[document_parameters("The data structure containing [`Result`] values.")]
167		///
168		#[document_returns(
169			"A pair of data structures: the first containing the [`Err`] values, and the second containing the [`Ok`] values."
170		)]
171		#[document_examples]
172		///
173		/// ```
174		/// use fp_library::{
175		/// 	brands::*,
176		/// 	functions::explicit::*,
177		/// };
178		///
179		/// let x: Option<Result<i32, &str>> = Some(Ok(5));
180		/// let (errs, oks) = separate::<OptionBrand, _, _, _, _>(x);
181		/// assert_eq!(oks, Some(5));
182		/// assert_eq!(errs, None);
183		///
184		/// let y: Option<Result<i32, &str>> = Some(Err("error"));
185		/// let (errs2, oks2) = separate::<OptionBrand, _, _, _, _>(y);
186		/// assert_eq!(oks2, None);
187		/// assert_eq!(errs2, Some("error"));
188		/// ```
189		fn separate<'a, E: 'a, O: 'a>(
190			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>)
191		) -> (
192			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
193			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
194		);
195	}
196
197	/// Compacts a data structure of [`Option`]s, discarding [`None`] values and keeping [`Some`] values.
198	///
199	/// Free function version that dispatches to [the type class' associated function][`Compactable::compact`].
200	#[document_signature]
201	///
202	#[document_type_parameters(
203		"The lifetime of the elements.",
204		"The brand of the compactable structure.",
205		"The type of the elements in the [`Option`]."
206	)]
207	///
208	#[document_parameters("The data structure containing [`Option`] values.")]
209	///
210	#[document_returns(
211		"A new data structure containing only the values from the [`Some`] variants."
212	)]
213	#[document_examples]
214	///
215	/// ```
216	/// use fp_library::{
217	/// 	brands::*,
218	/// 	functions::explicit::*,
219	/// };
220	///
221	/// let x = Some(Some(5));
222	/// let y = compact::<OptionBrand, _, _, _>(x);
223	/// assert_eq!(y, Some(5));
224	/// ```
225	pub fn compact<'a, Brand: Compactable, A: 'a>(
226		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<
227			'a,
228			Apply!(<OptionBrand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
229		>)
230	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
231		Brand::compact(fa)
232	}
233
234	/// Separates a data structure of [`Result`]s into two data structures: one containing the [`Err`] values and one containing the [`Ok`] values.
235	///
236	/// Free function version that dispatches to [the type class' associated function][`Compactable::separate`].
237	#[document_signature]
238	///
239	#[document_type_parameters(
240		"The lifetime of the elements.",
241		"The brand of the compactable structure.",
242		"The type of the error values.",
243		"The type of the success values."
244	)]
245	///
246	#[document_parameters("The data structure containing [`Result`] values.")]
247	///
248	#[document_returns(
249		"A pair of data structures: the first containing the [`Err`] values, and the second containing the [`Ok`] values."
250	)]
251	#[document_examples]
252	///
253	/// ```
254	/// use fp_library::{
255	/// 	brands::*,
256	/// 	functions::explicit::*,
257	/// };
258	///
259	/// let x: Option<Result<i32, &str>> = Some(Ok(5));
260	/// let (errs, oks) = separate::<OptionBrand, _, _, _, _>(x);
261	/// assert_eq!(oks, Some(5));
262	/// assert_eq!(errs, None);
263	/// ```
264	pub fn separate<'a, Brand: Compactable, E: 'a, O: 'a>(
265		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Result<O, E>>)
266	) -> (
267		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, E>),
268		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, O>),
269	) {
270		Brand::separate::<E, O>(fa)
271	}
272}
273
274pub use inner::*;