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