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::*;