frunk_core/coproduct.rs
1//! Module that holds Coproduct data structures, traits, and implementations
2//!
3//! Think of "Coproduct" as ad-hoc enums; allowing you to do something like this
4//!
5//! ```
6//! # fn main() {
7//! # use frunk_core::Coprod;
8//! // For simplicity, assign our Coproduct type to a type alias
9//! // This is purely optional.
10//! type I32Bool = Coprod!(i32, bool);
11//! // Inject things into our Coproduct type
12//! let co1 = I32Bool::inject(3);
13//! let co2 = I32Bool::inject(true);
14//!
15//! // Getting stuff
16//! let get_from_1a: Option<&i32> = co1.get();
17//! let get_from_1b: Option<&bool> = co1.get();
18//! assert_eq!(get_from_1a, Some(&3));
19//! assert_eq!(get_from_1b, None);
20//!
21//! let get_from_2a: Option<&i32> = co2.get();
22//! let get_from_2b: Option<&bool> = co2.get();
23//! assert_eq!(get_from_2a, None);
24//! assert_eq!(get_from_2b, Some(&true));
25//!
26//! // *Taking* stuff (by value)
27//! let take_from_1a: Option<i32> = co1.take();
28//! assert_eq!(take_from_1a, Some(3));
29//!
30//! // Or with a Result
31//! let uninject_from_1a: Result<i32, _> = co1.uninject();
32//! let uninject_from_1b: Result<bool, _> = co1.uninject();
33//! assert_eq!(uninject_from_1a, Ok(3));
34//! assert!(uninject_from_1b.is_err());
35//! # }
36//! ```
37//!
38//! Or, if you want to "fold" over all possible values of a coproduct
39//!
40//! ```
41//! # use frunk_core::{hlist, poly_fn, Coprod};
42//! # fn main() {
43//! # type I32Bool = Coprod!(i32, bool);
44//! # let co1 = I32Bool::inject(3);
45//! # let co2 = I32Bool::inject(true);
46//! // In the below, we use unreachable!() to make it obvious hat we know what type of
47//! // item is inside our coproducts co1 and co2 but in real life, you should be writing
48//! // complete functions for all the cases when folding coproducts
49//! //
50//! // to_ref borrows every item so that we can fold without consuming the coproduct.
51//! assert_eq!(
52//! co1.to_ref().fold(hlist![|&i| format!("i32 {}", i),
53//! |&b| unreachable!() /* we know this won't happen for co1 */ ]),
54//! "i32 3".to_string());
55//! assert_eq!(
56//! co2.to_ref().fold(hlist![|&i| unreachable!() /* we know this won't happen for co2 */,
57//! |&b| String::from(if b { "t" } else { "f" })]),
58//! "t".to_string());
59//!
60//! // Here, we use the poly_fn! macro to declare a polymorphic function to avoid caring
61//! // about the order in which declare handlers for the types in our coproduct
62//! let folded = co1.fold(
63//! poly_fn![
64//! |_b: bool| -> String { unreachable!() }, /* we know this won't happen for co1 */
65//! |i: i32 | -> String { format!("i32 {}", i) },
66//! ]
67//! );
68//! assert_eq!(folded, "i32 3".to_string());
69//! # }
70//! ```
71
72use crate::hlist::{HCons, HNil};
73use crate::indices::{Here, There};
74use crate::traits::{Func, Poly, ToMut, ToRef};
75#[cfg(feature = "serde")]
76use serde::{Deserialize, Serialize};
77
78/// Enum type representing a Coproduct. Think of this as a Result, but capable
79/// of supporting any arbitrary number of types instead of just 2.
80///
81/// To construct a Coproduct, you would typically declare a type using the `Coprod!` type
82/// macro and then use the `inject` method.
83///
84/// # Examples
85///
86/// ```
87/// # fn main() {
88/// use frunk_core::Coprod;
89///
90/// type I32Bool = Coprod!(i32, bool);
91/// let co1 = I32Bool::inject(3);
92/// let get_from_1a: Option<&i32> = co1.get();
93/// let get_from_1b: Option<&bool> = co1.get();
94/// assert_eq!(get_from_1a, Some(&3));
95/// assert_eq!(get_from_1b, None);
96/// # }
97/// ```
98#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100pub enum Coproduct<H, T> {
101 /// Coproduct is either H or T, in this case, it is H
102 Inl(H),
103 /// Coproduct is either H or T, in this case, it is T
104 Inr(T),
105}
106
107/// Phantom type for signature purposes only (has no value)
108///
109/// Used by the macro to terminate the Coproduct type signature
110#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
111#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
112pub enum CNil {}
113
114// Inherent methods
115impl<Head, Tail> Coproduct<Head, Tail> {
116 /// Instantiate a coproduct from an element.
117 ///
118 /// This is generally much nicer than nested usage of `Coproduct::{Inl, Inr}`.
119 /// The method uses a trick with type inference to automatically build the correct variant
120 /// according to the input type.
121 ///
122 /// In standard usage, the `Index` type parameter can be ignored,
123 /// as it will typically be solved for using type inference.
124 ///
125 /// # Rules
126 ///
127 /// If the type does not appear in the coproduct, the conversion is forbidden.
128 ///
129 /// If the type appears multiple times in the coproduct, type inference will fail.
130 ///
131 /// # Example
132 ///
133 /// ```
134 /// # fn main() {
135 /// use frunk::Coproduct;
136 /// use frunk_core::Coprod;
137 ///
138 /// type I32F32 = Coprod!(i32, f32);
139 ///
140 /// // Constructing coproducts using inject:
141 /// let co1_nice: I32F32 = Coproduct::inject(1i32);
142 /// let co2_nice: I32F32 = Coproduct::inject(42f32);
143 ///
144 /// // Compare this to the "hard way":
145 /// let co1_ugly: I32F32 = Coproduct::Inl(1i32);
146 /// let co2_ugly: I32F32 = Coproduct::Inr(Coproduct::Inl(42f32));
147 ///
148 /// assert_eq!(co1_nice, co1_ugly);
149 /// assert_eq!(co2_nice, co2_ugly);
150 ///
151 /// // Feel free to use `inject` on a type alias, or even directly on the
152 /// // `Coprod!` macro. (the latter requires wrapping the type in `<>`)
153 /// let _ = I32F32::inject(42f32);
154 /// let _ = <Coprod!(i32, f32)>::inject(42f32);
155 ///
156 /// // You can also use a turbofish to specify the type of the input when
157 /// // it is ambiguous (e.g. an empty `vec![]`).
158 /// // The Index parameter should be left as `_`.
159 /// type Vi32Vf32 = Coprod!(Vec<i32>, Vec<f32>);
160 /// let _: Vi32Vf32 = Coproduct::inject::<Vec<i32>, _>(vec![]);
161 /// # }
162 /// ```
163 #[inline(always)]
164 pub fn inject<T, Index>(to_insert: T) -> Self
165 where
166 Self: CoprodInjector<T, Index>,
167 {
168 CoprodInjector::inject(to_insert)
169 }
170
171 /// Borrow an element from a coproduct by type.
172 ///
173 /// # Example
174 ///
175 /// ```
176 /// # fn main() {
177 /// use frunk_core::Coprod;
178 ///
179 /// type I32F32 = Coprod!(i32, f32);
180 ///
181 /// // You can let type inference find the desired type:
182 /// let co1 = I32F32::inject(42f32);
183 /// let co1_as_i32: Option<&i32> = co1.get();
184 /// let co1_as_f32: Option<&f32> = co1.get();
185 /// assert_eq!(co1_as_i32, None);
186 /// assert_eq!(co1_as_f32, Some(&42f32));
187 ///
188 /// // You can also use turbofish syntax to specify the type.
189 /// // The Index parameter should be left as `_`.
190 /// let co2 = I32F32::inject(1i32);
191 /// assert_eq!(co2.get::<i32, _>(), Some(&1));
192 /// assert_eq!(co2.get::<f32, _>(), None);
193 /// # }
194 /// ```
195 #[inline(always)]
196 pub fn get<S, Index>(&self) -> Option<&S>
197 where
198 Self: CoproductSelector<S, Index>,
199 {
200 CoproductSelector::get(self)
201 }
202
203 /// Retrieve an element from a coproduct by type, ignoring all others.
204 ///
205 /// # Example
206 ///
207 /// ```
208 /// # fn main() {
209 /// use frunk_core::Coprod;
210 ///
211 /// type I32F32 = Coprod!(i32, f32);
212 ///
213 /// // You can let type inference find the desired type:
214 /// let co1 = I32F32::inject(42f32);
215 /// let co1_as_i32: Option<i32> = co1.take();
216 /// let co1_as_f32: Option<f32> = co1.take();
217 /// assert_eq!(co1_as_i32, None);
218 /// assert_eq!(co1_as_f32, Some(42f32));
219 ///
220 /// // You can also use turbofish syntax to specify the type.
221 /// // The Index parameter should be left as `_`.
222 /// let co2 = I32F32::inject(1i32);
223 /// assert_eq!(co2.take::<i32, _>(), Some(1));
224 /// assert_eq!(co2.take::<f32, _>(), None);
225 /// # }
226 /// ```
227 #[inline(always)]
228 pub fn take<T, Index>(self) -> Option<T>
229 where
230 Self: CoproductTaker<T, Index>,
231 {
232 CoproductTaker::take(self)
233 }
234
235 /// Attempt to extract a value from a coproduct (or get the remaining possibilities).
236 ///
237 /// By chaining calls to this, one can exhaustively match all variants of a coproduct.
238 ///
239 /// # Examples
240 ///
241 /// Basic usage:
242 ///
243 /// ```
244 /// # fn main() {
245 /// use frunk_core::Coprod;
246 ///
247 /// type I32F32 = Coprod!(i32, f32);
248 /// type I32 = Coprod!(i32); // remainder after uninjecting f32
249 /// type F32 = Coprod!(f32); // remainder after uninjecting i32
250 ///
251 /// let co1 = I32F32::inject(42f32);
252 ///
253 /// // You can let type inference find the desired type.
254 /// let co1 = I32F32::inject(42f32);
255 /// let co1_as_i32: Result<i32, F32> = co1.uninject();
256 /// let co1_as_f32: Result<f32, I32> = co1.uninject();
257 /// assert_eq!(co1_as_i32, Err(F32::inject(42f32)));
258 /// assert_eq!(co1_as_f32, Ok(42f32));
259 ///
260 /// // It is not necessary to annotate the type of the remainder:
261 /// let res: Result<i32, _> = co1.uninject();
262 /// assert!(res.is_err());
263 ///
264 /// // You can also use turbofish syntax to specify the type.
265 /// // The Index parameter should be left as `_`.
266 /// let co2 = I32F32::inject(1i32);
267 /// assert_eq!(co2.uninject::<i32, _>(), Ok(1));
268 /// assert_eq!(co2.uninject::<f32, _>(), Err(I32::inject(1)));
269 /// # }
270 /// ```
271 ///
272 /// Chaining calls for an exhaustive match:
273 ///
274 /// ```rust
275 /// # fn main() {
276 /// use frunk_core::Coprod;
277 ///
278 /// type I32F32 = Coprod!(i32, f32);
279 ///
280 /// // Be aware that this particular example could be
281 /// // written far more succinctly using `fold`.
282 /// fn handle_i32_f32(co: I32F32) -> &'static str {
283 /// // Remove i32 from the coproduct
284 /// let co = match co.uninject::<i32, _>() {
285 /// Ok(x) => return "integer!",
286 /// Err(co) => co,
287 /// };
288 ///
289 /// // Remove f32 from the coproduct
290 /// let co = match co.uninject::<f32, _>() {
291 /// Ok(x) => return "float!",
292 /// Err(co) => co,
293 /// };
294 ///
295 /// // Now co is empty
296 /// match co { /* unreachable */ }
297 /// }
298 ///
299 /// assert_eq!(handle_i32_f32(I32F32::inject(3)), "integer!");
300 /// assert_eq!(handle_i32_f32(I32F32::inject(3.0)), "float!");
301 /// # }
302 #[inline(always)]
303 pub fn uninject<T, Index>(self) -> Result<T, <Self as CoprodUninjector<T, Index>>::Remainder>
304 where
305 Self: CoprodUninjector<T, Index>,
306 {
307 CoprodUninjector::uninject(self)
308 }
309
310 /// Extract a subset of the possible types in a coproduct (or get the remaining possibilities)
311 ///
312 /// This is basically [`uninject`] on steroids. It lets you remove a number
313 /// of types from a coproduct at once, leaving behind the remainder in an `Err`.
314 /// For instance, one can extract `Coprod!(C, A)` from `Coprod!(A, B, C, D)`
315 /// to produce `Result<Coprod!(C, A), Coprod!(B, D)>`.
316 ///
317 /// Each type in the extracted subset is required to be part of the input coproduct.
318 ///
319 /// [`uninject`]: #method.uninject
320 ///
321 /// # Example
322 ///
323 /// Basic usage:
324 ///
325 /// ```
326 /// # fn main() {
327 /// use frunk_core::Coprod;
328 ///
329 /// type I32BoolF32 = Coprod!(i32, bool, f32);
330 /// type I32F32 = Coprod!(i32, f32);
331 ///
332 /// let co1 = I32BoolF32::inject(42_f32);
333 /// let co2 = I32BoolF32::inject(true);
334 ///
335 /// let sub1: Result<Coprod!(i32, f32), _> = co1.subset();
336 /// let sub2: Result<Coprod!(i32, f32), _> = co2.subset();
337 /// assert!(sub1.is_ok());
338 /// assert!(sub2.is_err());
339 ///
340 /// // Turbofish syntax for specifying the target subset is also supported.
341 /// // The Indices parameter should be left to type inference using `_`.
342 /// assert!(co1.subset::<Coprod!(i32, f32), _>().is_ok());
343 /// assert!(co2.subset::<Coprod!(i32, f32), _>().is_err());
344 ///
345 /// // Order doesn't matter.
346 /// assert!(co1.subset::<Coprod!(f32, i32), _>().is_ok());
347 /// # }
348 /// ```
349 ///
350 /// Like `uninject`, `subset` can be used for exhaustive matching,
351 /// with the advantage that it can remove more than one type at a time:
352 ///
353 /// ```
354 /// # fn main() {
355 /// use frunk_core::{Coprod, hlist};
356 /// use frunk_core::coproduct::Coproduct;
357 ///
358 /// fn handle_stringly_things(co: Coprod!(&'static str, String)) -> String {
359 /// co.fold(hlist![
360 /// |s| format!("&str {}", s),
361 /// |s| format!("String {}", s),
362 /// ])
363 /// }
364 ///
365 /// fn handle_countly_things(co: Coprod!(u32)) -> String {
366 /// co.fold(hlist![
367 /// |n| vec!["."; n as usize].concat(),
368 /// ])
369 /// }
370 ///
371 /// fn handle_all(co: Coprod!(String, u32, &'static str)) -> String {
372 /// // co is currently Coprod!(String, u32, &'static str)
373 /// let co = match co.subset().map(handle_stringly_things) {
374 /// Ok(s) => return s,
375 /// Err(co) => co,
376 /// };
377 ///
378 /// // Now co is Coprod!(u32).
379 /// let co = match co.subset().map(handle_countly_things) {
380 /// Ok(s) => return s,
381 /// Err(co) => co,
382 /// };
383 ///
384 /// // Now co is empty.
385 /// match co { /* unreachable */ }
386 /// }
387 ///
388 /// assert_eq!(handle_all(Coproduct::inject("hello")), "&str hello");
389 /// assert_eq!(handle_all(Coproduct::inject(String::from("World!"))), "String World!");
390 /// assert_eq!(handle_all(Coproduct::inject(4)), "....");
391 /// # }
392 /// ```
393 #[inline(always)]
394 pub fn subset<Targets, Indices>(
395 self,
396 ) -> Result<Targets, <Self as CoproductSubsetter<Targets, Indices>>::Remainder>
397 where
398 Self: CoproductSubsetter<Targets, Indices>,
399 {
400 CoproductSubsetter::subset(self)
401 }
402
403 /// Convert a coproduct into another that can hold its variants.
404 ///
405 /// This converts a coproduct into another one which is capable of holding each
406 /// of its types. The most well-supported use-cases (i.e. those where type inference
407 /// is capable of solving for the indices) are:
408 ///
409 /// * Reordering variants: `Coprod!(C, A, B) -> Coprod!(A, B, C)`
410 /// * Embedding into a superset: `Coprod!(B, D) -> Coprod!(A, B, C, D, E)`
411 /// * Coalescing duplicate inputs: `Coprod!(B, B, B, B) -> Coprod!(A, B, C)`
412 ///
413 /// and of course any combination thereof.
414 ///
415 /// # Rules
416 ///
417 /// If any type in the input does not appear in the output, the conversion is forbidden.
418 ///
419 /// If any type in the input appears multiple times in the output, type inference will fail.
420 ///
421 /// All of these rules fall naturally out of its fairly simple definition,
422 /// which is equivalent to:
423 ///
424 /// ```text
425 /// coprod.fold(hlist![
426 /// |x| Coproduct::inject(x),
427 /// |x| Coproduct::inject(x),
428 /// ...
429 /// |x| Coproduct::inject(x),
430 /// ])
431 /// ```
432 ///
433 /// # Example
434 ///
435 /// ```
436 /// # fn main() {
437 /// use frunk_core::Coprod;
438 ///
439 /// type I32BoolF32 = Coprod!(i32, bool, f32);
440 /// type BoolI32 = Coprod!(bool, i32);
441 ///
442 /// let co = BoolI32::inject(true);
443 /// let embedded: I32BoolF32 = co.embed();
444 /// assert_eq!(embedded, I32BoolF32::inject(true));
445 ///
446 /// // Turbofish syntax for specifying the output type is also supported.
447 /// // The Indices parameter should be left to type inference using `_`.
448 /// let embedded = co.embed::<I32BoolF32, _>();
449 /// assert_eq!(embedded, I32BoolF32::inject(true));
450 /// # }
451 /// ```
452 #[inline(always)]
453 pub fn embed<Targets, Indices>(self) -> Targets
454 where
455 Self: CoproductEmbedder<Targets, Indices>,
456 {
457 CoproductEmbedder::embed(self)
458 }
459
460 /// Borrow each variant of the Coproduct.
461 ///
462 /// # Example
463 ///
464 /// Composing with `subset` to match a subset of variants without
465 /// consuming the coproduct:
466 ///
467 /// ```
468 /// # fn main() {
469 /// use frunk::Coproduct;
470 /// use frunk_core::Coprod;
471 ///
472 /// let co: Coprod!(i32, bool, String) = Coproduct::inject(true);
473 ///
474 /// assert!(co.to_ref().subset::<Coprod!(&bool, &String), _>().is_ok());
475 /// # }
476 /// ```
477 #[inline(always)]
478 pub fn to_ref<'a>(&'a self) -> <Self as ToRef<'a>>::Output
479 where
480 Self: ToRef<'a>,
481 {
482 ToRef::to_ref(self)
483 }
484
485 /// Borrow each variant of the `Coproduct` mutably.
486 ///
487 /// # Example
488 ///
489 /// Composing with `subset` to match a subset of variants without
490 /// consuming the coproduct:
491 ///
492 /// ```
493 /// # fn main() {
494 /// use frunk::Coproduct;
495 /// use frunk_core::Coprod;
496 ///
497 /// let mut co: Coprod!(i32, bool, String) = Coproduct::inject(true);
498 ///
499 /// assert!(co.to_mut().subset::<Coprod!(&mut bool, &mut String), _>().is_ok());
500 /// # }
501 /// ```
502 #[inline(always)]
503 pub fn to_mut<'a>(&'a mut self) -> <Self as ToMut<'a>>::Output
504 where
505 Self: ToMut<'a>,
506 {
507 ToMut::to_mut(self)
508 }
509
510 /// Use functions to transform a Coproduct into a single value.
511 ///
512 /// A variety of types are supported for the `Folder` argument:
513 ///
514 /// * An `hlist![]` of closures (one for each type, in order).
515 /// * A single closure (for a Coproduct that is homogenous).
516 /// * A single [`Poly`].
517 ///
518 /// [`Poly`]: ../traits/struct.Poly.html
519 ///
520 /// # Example
521 ///
522 /// ```
523 /// # fn main() {
524 /// use frunk_core::{Coprod, hlist};
525 ///
526 /// type I32F32Bool = Coprod!(i32, f32, bool);
527 ///
528 /// let co1 = I32F32Bool::inject(3);
529 /// let co2 = I32F32Bool::inject(true);
530 /// let co3 = I32F32Bool::inject(42f32);
531 ///
532 /// let folder = hlist![|&i| format!("int {}", i),
533 /// |&f| format!("float {}", f),
534 /// |&b| (if b { "t" } else { "f" }).to_string()];
535 ///
536 /// assert_eq!(co1.to_ref().fold(folder), "int 3".to_string());
537 /// # }
538 /// ```
539 ///
540 /// Using a polymorphic function type has the advantage of not
541 /// forcing you to care about the order in which you declare
542 /// handlers for the types in your Coproduct.
543 ///
544 /// ```
545 /// # fn main() {
546 /// use frunk::{Poly, Func};
547 /// use frunk_core::Coprod;
548 ///
549 /// type I32F32Bool = Coprod!(i32, f32, bool);
550 ///
551 /// impl Func<i32> for P {
552 /// type Output = bool;
553 /// fn call(args: i32) -> Self::Output {
554 /// args > 100
555 /// }
556 /// }
557 /// impl Func<bool> for P {
558 /// type Output = bool;
559 /// fn call(args: bool) -> Self::Output {
560 /// args
561 /// }
562 /// }
563 /// impl Func<f32> for P {
564 /// type Output = bool;
565 /// fn call(args: f32) -> Self::Output {
566 /// args > 9000f32
567 /// }
568 /// }
569 /// struct P;
570 ///
571 /// let co1 = I32F32Bool::inject(3);
572 /// let folded = co1.fold(Poly(P));
573 /// # }
574 /// ```
575 #[inline(always)]
576 pub fn fold<Output, Folder>(self, folder: Folder) -> Output
577 where
578 Self: CoproductFoldable<Folder, Output>,
579 {
580 CoproductFoldable::fold(self, folder)
581 }
582
583 /// Apply a function to each variant of a Coproduct.
584 ///
585 /// The transforms some `Coprod!(A, B, C, ..., E)` into some
586 /// `Coprod!(T, U, V, ..., Z)`. A variety of types are supported for the
587 /// mapper argument:
588 ///
589 /// * An `hlist![]` of closures (one for each variant).
590 /// * A single closure (for mapping a Coproduct that is homogenous).
591 /// * A single [`Poly`].
592 ///
593 /// # Examples
594 ///
595 /// ```
596 /// use frunk::{hlist, Coprod};
597 ///
598 /// type I32F32Bool = Coprod!(i32, f32, bool);
599 /// type BoolStrU8 = Coprod!(bool, &'static str, u8);
600 ///
601 /// let co1 = I32F32Bool::inject(3);
602 /// let co2 = I32F32Bool::inject(42f32);
603 /// let co3 = I32F32Bool::inject(true);
604 ///
605 /// let mapper = hlist![
606 /// |n| n > 0,
607 /// |f| if f == 42f32 { "๐" } else { "๐คจ" },
608 /// |b| if b { 1u8 } else { 0u8 },
609 /// ];
610 ///
611 /// assert_eq!(co1.map(&mapper), BoolStrU8::inject(true));
612 /// assert_eq!(co2.map(&mapper), BoolStrU8::inject("๐"));
613 /// assert_eq!(co3.map(&mapper), BoolStrU8::inject(1u8));
614 /// ```
615 ///
616 /// Using a polymorphic function type has the advantage of not forcing you
617 /// to care about the order in which you declare handlers for the types in
618 /// your Coproduct.
619 ///
620 /// ```
621 /// use frunk::{poly_fn, Coprod};
622 ///
623 /// type I32F32Bool = Coprod!(i32, f32, bool);
624 ///
625 /// let co1 = I32F32Bool::inject(3);
626 /// let co2 = I32F32Bool::inject(42f32);
627 /// let co3 = I32F32Bool::inject(true);
628 ///
629 /// let mapper = poly_fn![
630 /// |b: bool| -> bool { !b },
631 /// |n: i32| -> i32 { n + 3 },
632 /// |f: f32| -> f32 { -f },
633 /// ];
634 ///
635 /// assert_eq!(co1.map(&mapper), I32F32Bool::inject(6));
636 /// assert_eq!(co2.map(&mapper), I32F32Bool::inject(-42f32));
637 /// assert_eq!(co3.map(&mapper), I32F32Bool::inject(false));
638 /// ```
639 ///
640 /// You can also use a singular closure if the Coproduct variants are all
641 /// the same.
642 ///
643 /// ```
644 /// use frunk::Coprod;
645 ///
646 /// type IntInt = Coprod!(i32, i32);
647 /// type BoolBool = Coprod!(bool, bool);
648 ///
649 /// let mapper = |n| n > 0;
650 ///
651 /// let co = IntInt::Inl(42);
652 /// assert_eq!(co.map(mapper), BoolBool::Inl(true));
653 /// ```
654 #[inline(always)]
655 pub fn map<F>(self, mapper: F) -> <Self as CoproductMappable<F>>::Output
656 where
657 Self: CoproductMappable<F>,
658 {
659 CoproductMappable::map(self, mapper)
660 }
661}
662
663impl<T> Coproduct<T, CNil> {
664 /// Extract the value from a coproduct with only one variant.
665 ///
666 /// # Example
667 ///
668 /// ```
669 /// # fn main() {
670 /// use frunk_core::Coprod;
671 ///
672 /// type I32Only = Coprod!(i32);
673 /// let co = I32Only::inject(5);
674 ///
675 /// assert_eq!(co.extract(), 5);
676 /// # }
677 /// ```
678 #[inline(always)]
679 pub fn extract(self) -> T {
680 match self {
681 Coproduct::Inl(v) => v,
682 Coproduct::Inr(never) => match never {},
683 }
684 }
685}
686
687/// Trait for instantiating a coproduct from an element
688///
689/// This trait is part of the implementation of the inherent static method
690/// [`Coproduct::inject`]. Please see that method for more information.
691///
692/// You only need to import this trait when working with generic
693/// Coproducts of unknown type. In most code, `Coproduct::inject` will
694/// "just work," with or without this trait.
695///
696/// [`Coproduct::inject`]: enum.Coproduct.html#method.inject
697pub trait CoprodInjector<InjectType, Index> {
698 /// Instantiate a coproduct from an element.
699 ///
700 /// Please see the [inherent static method] for more information.
701 ///
702 /// The only difference between that inherent method and this
703 /// trait method is the location of the type parameters.
704 /// (here, they are on the trait rather than the method)
705 ///
706 /// [inherent static method]: enum.Coproduct.html#method.inject
707 fn inject(to_insert: InjectType) -> Self;
708}
709
710impl<I, Tail> CoprodInjector<I, Here> for Coproduct<I, Tail> {
711 fn inject(to_insert: I) -> Self {
712 Coproduct::Inl(to_insert)
713 }
714}
715
716impl<Head, I, Tail, TailIndex> CoprodInjector<I, There<TailIndex>> for Coproduct<Head, Tail>
717where
718 Tail: CoprodInjector<I, TailIndex>,
719{
720 fn inject(to_insert: I) -> Self {
721 let tail_inserted = <Tail as CoprodInjector<I, TailIndex>>::inject(to_insert);
722 Coproduct::Inr(tail_inserted)
723 }
724}
725
726// For turning something into a Coproduct -->
727
728/// Trait for borrowing a coproduct element by type
729///
730/// This trait is part of the implementation of the inherent method
731/// [`Coproduct::get`]. Please see that method for more information.
732///
733/// You only need to import this trait when working with generic
734/// Coproducts of unknown type. If you have a Coproduct of known type,
735/// then `co.get()` should "just work" even without the trait.
736///
737/// [`Coproduct::get`]: enum.Coproduct.html#method.get
738pub trait CoproductSelector<S, I> {
739 /// Borrow an element from a coproduct by type.
740 ///
741 /// Please see the [inherent method] for more information.
742 ///
743 /// The only difference between that inherent method and this
744 /// trait method is the location of the type parameters.
745 /// (here, they are on the trait rather than the method)
746 ///
747 /// [inherent method]: enum.Coproduct.html#method.get
748 fn get(&self) -> Option<&S>;
749}
750
751impl<Head, Tail> CoproductSelector<Head, Here> for Coproduct<Head, Tail> {
752 fn get(&self) -> Option<&Head> {
753 use self::Coproduct::*;
754 match *self {
755 Inl(ref thing) => Some(thing),
756 _ => None, // Impossible
757 }
758 }
759}
760
761impl<Head, FromTail, Tail, TailIndex> CoproductSelector<FromTail, There<TailIndex>>
762 for Coproduct<Head, Tail>
763where
764 Tail: CoproductSelector<FromTail, TailIndex>,
765{
766 fn get(&self) -> Option<&FromTail> {
767 use self::Coproduct::*;
768 match *self {
769 Inr(ref rest) => rest.get(),
770 _ => None, // Impossible
771 }
772 }
773}
774
775/// Trait for retrieving a coproduct element by type
776///
777/// This trait is part of the implementation of the inherent method
778/// [`Coproduct::take`]. Please see that method for more information.
779///
780/// You only need to import this trait when working with generic
781/// Coproducts of unknown type. If you have a Coproduct of known type,
782/// then `co.take()` should "just work" even without the trait.
783///
784/// [`Coproduct::take`]: enum.Coproduct.html#method.take
785pub trait CoproductTaker<S, I> {
786 /// Retrieve an element from a coproduct by type, ignoring all others.
787 ///
788 /// Please see the [inherent method] for more information.
789 ///
790 /// The only difference between that inherent method and this
791 /// trait method is the location of the type parameters.
792 /// (here, they are on the trait rather than the method)
793 ///
794 /// [inherent method]: enum.Coproduct.html#method.take
795 fn take(self) -> Option<S>;
796}
797
798impl<Head, Tail> CoproductTaker<Head, Here> for Coproduct<Head, Tail> {
799 fn take(self) -> Option<Head> {
800 use self::Coproduct::*;
801 match self {
802 Inl(thing) => Some(thing),
803 _ => None, // Impossible
804 }
805 }
806}
807
808impl<Head, FromTail, Tail, TailIndex> CoproductTaker<FromTail, There<TailIndex>>
809 for Coproduct<Head, Tail>
810where
811 Tail: CoproductTaker<FromTail, TailIndex>,
812{
813 fn take(self) -> Option<FromTail> {
814 use self::Coproduct::*;
815 match self {
816 Inr(rest) => rest.take(),
817 _ => None, // Impossible
818 }
819 }
820}
821
822/// Trait for folding a coproduct into a single value.
823///
824/// This trait is part of the implementation of the inherent method
825/// [`Coproduct::fold`]. Please see that method for more information.
826///
827/// You only need to import this trait when working with generic
828/// Coproducts or Folders of unknown type. If the type of everything is known,
829/// then `co.fold(folder)` should "just work" even without the trait.
830///
831/// [`Coproduct::fold`]: enum.Coproduct.html#method.fold
832pub trait CoproductFoldable<Folder, Output> {
833 /// Use functions to fold a coproduct into a single value.
834 ///
835 /// Please see the [inherent method] for more information.
836 ///
837 /// The only difference between that inherent method and this
838 /// trait method is the location of the type parameters.
839 /// (here, they are on the trait rather than the method)
840 ///
841 /// [inherent method]: enum.Coproduct.html#method.fold
842 fn fold(self, f: Folder) -> Output;
843}
844
845impl<P, R, CH, CTail> CoproductFoldable<Poly<P>, R> for Coproduct<CH, CTail>
846where
847 P: Func<CH, Output = R>,
848 CTail: CoproductFoldable<Poly<P>, R>,
849{
850 fn fold(self, f: Poly<P>) -> R {
851 use self::Coproduct::*;
852 match self {
853 Inl(r) => P::call(r),
854 Inr(rest) => rest.fold(f),
855 }
856 }
857}
858
859impl<F, R, FTail, CH, CTail> CoproductFoldable<HCons<F, FTail>, R> for Coproduct<CH, CTail>
860where
861 F: FnOnce(CH) -> R,
862 CTail: CoproductFoldable<FTail, R>,
863{
864 fn fold(self, f: HCons<F, FTail>) -> R {
865 use self::Coproduct::*;
866 let f_head = f.head;
867 let f_tail = f.tail;
868 match self {
869 Inl(r) => (f_head)(r),
870 Inr(rest) => rest.fold(f_tail),
871 }
872 }
873}
874
875/// This is literally impossible; CNil is not instantiable
876impl<F, R> CoproductFoldable<F, R> for CNil {
877 fn fold(self, _: F) -> R {
878 unreachable!()
879 }
880}
881
882/// Trait for mapping over a coproduct's variants.
883///
884/// This trait is part of the implementation of the inherent method
885/// [`Coproduct::map`]. Please see that method for more information.
886///
887/// You only need to import this trait when working with generic Coproducts or
888/// mappers of unknown type. If the type of everything is known, then
889/// `co.map(mapper)` should "just work" even without the trait.
890pub trait CoproductMappable<Mapper> {
891 type Output;
892
893 /// Use functions to map each variant of a coproduct.
894 ///
895 /// Please see the [inherent method] for more information.
896 ///
897 /// The only difference between that inherent method and this
898 /// trait method is the location of the type parameters.
899 /// (here, they are on the trait rather than the method)
900 ///
901 /// [inherent method]: Coproduct::map
902 fn map(self, f: Mapper) -> Self::Output;
903}
904
905/// Implementation for mapping a Coproduct using an `hlist!`.
906impl<F, R, MapperTail, CH, CTail> CoproductMappable<HCons<F, MapperTail>> for Coproduct<CH, CTail>
907where
908 F: FnOnce(CH) -> R,
909 CTail: CoproductMappable<MapperTail>,
910{
911 type Output = Coproduct<R, <CTail as CoproductMappable<MapperTail>>::Output>;
912
913 #[inline]
914 fn map(self, mapper: HCons<F, MapperTail>) -> Self::Output {
915 match self {
916 Coproduct::Inl(l) => Coproduct::Inl((mapper.head)(l)),
917 Coproduct::Inr(rest) => Coproduct::Inr(rest.map(mapper.tail)),
918 }
919 }
920}
921
922/// Implementation for mapping a Coproduct using a `&hlist!`.
923impl<'a, F, R, MapperTail, CH, CTail> CoproductMappable<&'a HCons<F, MapperTail>>
924 for Coproduct<CH, CTail>
925where
926 F: Fn(CH) -> R,
927 CTail: CoproductMappable<&'a MapperTail>,
928{
929 type Output = Coproduct<R, <CTail as CoproductMappable<&'a MapperTail>>::Output>;
930
931 #[inline]
932 fn map(self, mapper: &'a HCons<F, MapperTail>) -> Self::Output {
933 match self {
934 Coproduct::Inl(l) => Coproduct::Inl((mapper.head)(l)),
935 Coproduct::Inr(rest) => Coproduct::Inr(rest.map(&mapper.tail)),
936 }
937 }
938}
939
940/// Implementation for mapping a Coproduct using a `&mut hlist!`.
941impl<'a, F, R, MapperTail, CH, CTail> CoproductMappable<&'a mut HCons<F, MapperTail>>
942 for Coproduct<CH, CTail>
943where
944 F: FnMut(CH) -> R,
945 CTail: CoproductMappable<&'a mut MapperTail>,
946{
947 type Output = Coproduct<R, <CTail as CoproductMappable<&'a mut MapperTail>>::Output>;
948
949 #[inline]
950 fn map(self, mapper: &'a mut HCons<F, MapperTail>) -> Self::Output {
951 match self {
952 Coproduct::Inl(l) => Coproduct::Inl((mapper.head)(l)),
953 Coproduct::Inr(rest) => Coproduct::Inr(rest.map(&mut mapper.tail)),
954 }
955 }
956}
957
958/// Implementation for mapping a Coproduct using a `poly_fn!`.
959impl<P, CH, CTail> CoproductMappable<Poly<P>> for Coproduct<CH, CTail>
960where
961 P: Func<CH>,
962 CTail: CoproductMappable<Poly<P>>,
963{
964 type Output = Coproduct<<P as Func<CH>>::Output, <CTail as CoproductMappable<Poly<P>>>::Output>;
965
966 #[inline]
967 fn map(self, poly: Poly<P>) -> Self::Output {
968 match self {
969 Coproduct::Inl(l) => Coproduct::Inl(P::call(l)),
970 Coproduct::Inr(rest) => Coproduct::Inr(rest.map(poly)),
971 }
972 }
973}
974
975/// Implementation for mapping a Coproduct using a `&poly_fn!`.
976impl<'a, P, CH, CTail> CoproductMappable<&'a Poly<P>> for Coproduct<CH, CTail>
977where
978 P: Func<CH>,
979 CTail: CoproductMappable<&'a Poly<P>>,
980{
981 type Output =
982 Coproduct<<P as Func<CH>>::Output, <CTail as CoproductMappable<&'a Poly<P>>>::Output>;
983
984 #[inline]
985 fn map(self, poly: &'a Poly<P>) -> Self::Output {
986 match self {
987 Coproduct::Inl(l) => Coproduct::Inl(P::call(l)),
988 Coproduct::Inr(rest) => Coproduct::Inr(rest.map(poly)),
989 }
990 }
991}
992
993/// Implementation for mapping a Coproduct using a `&mut poly_fn!`.
994impl<'a, P, CH, CTail> CoproductMappable<&'a mut Poly<P>> for Coproduct<CH, CTail>
995where
996 P: Func<CH>,
997 CTail: CoproductMappable<&'a mut Poly<P>>,
998{
999 type Output =
1000 Coproduct<<P as Func<CH>>::Output, <CTail as CoproductMappable<&'a mut Poly<P>>>::Output>;
1001
1002 #[inline]
1003 fn map(self, poly: &'a mut Poly<P>) -> Self::Output {
1004 match self {
1005 Coproduct::Inl(l) => Coproduct::Inl(P::call(l)),
1006 Coproduct::Inr(rest) => Coproduct::Inr(rest.map(poly)),
1007 }
1008 }
1009}
1010
1011/// Implementation for mapping a Coproduct using a single function that can
1012/// handle all variants.
1013impl<F, R, CH, CTail> CoproductMappable<F> for Coproduct<CH, CTail>
1014where
1015 F: FnMut(CH) -> R,
1016 CTail: CoproductMappable<F>,
1017{
1018 type Output = Coproduct<R, <CTail as CoproductMappable<F>>::Output>;
1019
1020 #[inline]
1021 fn map(self, mut f: F) -> Self::Output {
1022 match self {
1023 Coproduct::Inl(l) => Coproduct::Inl(f(l)),
1024 Coproduct::Inr(rest) => Coproduct::Inr(rest.map(f)),
1025 }
1026 }
1027}
1028
1029/// Base case map impl.
1030impl<F> CoproductMappable<F> for CNil {
1031 type Output = CNil;
1032
1033 #[inline(always)]
1034 fn map(self, _: F) -> Self::Output {
1035 match self {}
1036 }
1037}
1038
1039impl<'a, CH: 'a, CTail> ToRef<'a> for Coproduct<CH, CTail>
1040where
1041 CTail: ToRef<'a>,
1042{
1043 type Output = Coproduct<&'a CH, <CTail as ToRef<'a>>::Output>;
1044
1045 #[inline(always)]
1046 fn to_ref(&'a self) -> Self::Output {
1047 match *self {
1048 Coproduct::Inl(ref r) => Coproduct::Inl(r),
1049 Coproduct::Inr(ref rest) => Coproduct::Inr(rest.to_ref()),
1050 }
1051 }
1052}
1053
1054impl<'a> ToRef<'a> for CNil {
1055 type Output = CNil;
1056
1057 fn to_ref(&'a self) -> CNil {
1058 match *self {}
1059 }
1060}
1061
1062impl<'a, CH: 'a, CTail> ToMut<'a> for Coproduct<CH, CTail>
1063where
1064 CTail: ToMut<'a>,
1065{
1066 type Output = Coproduct<&'a mut CH, <CTail as ToMut<'a>>::Output>;
1067
1068 #[inline(always)]
1069 fn to_mut(&'a mut self) -> Self::Output {
1070 match *self {
1071 Coproduct::Inl(ref mut r) => Coproduct::Inl(r),
1072 Coproduct::Inr(ref mut rest) => Coproduct::Inr(rest.to_mut()),
1073 }
1074 }
1075}
1076
1077impl<'a> ToMut<'a> for CNil {
1078 type Output = CNil;
1079
1080 fn to_mut(&'a mut self) -> CNil {
1081 match *self {}
1082 }
1083}
1084
1085/// Trait for extracting a value from a coproduct in an exhaustive way.
1086///
1087/// This trait is part of the implementation of the inherent method
1088/// [`Coproduct::uninject`]. Please see that method for more information.
1089///
1090/// You only need to import this trait when working with generic
1091/// Coproducts of unknown type. If you have a Coproduct of known type,
1092/// then `co.uninject()` should "just work" even without the trait.
1093///
1094/// [`Coproduct::uninject`]: enum.Coproduct.html#method.uninject
1095pub trait CoprodUninjector<T, Idx>: CoprodInjector<T, Idx> {
1096 type Remainder;
1097
1098 /// Attempt to extract a value from a coproduct (or get the remaining possibilities).
1099 ///
1100 /// Please see the [inherent method] for more information.
1101 ///
1102 /// The only difference between that inherent method and this
1103 /// trait method is the location of the type parameters.
1104 /// (here, they are on the trait rather than the method)
1105 ///
1106 /// [inherent method]: enum.Coproduct.html#method.uninject
1107 fn uninject(self) -> Result<T, Self::Remainder>;
1108}
1109
1110impl<Hd, Tl> CoprodUninjector<Hd, Here> for Coproduct<Hd, Tl> {
1111 type Remainder = Tl;
1112
1113 fn uninject(self) -> Result<Hd, Tl> {
1114 match self {
1115 Coproduct::Inl(h) => Ok(h),
1116 Coproduct::Inr(t) => Err(t),
1117 }
1118 }
1119}
1120
1121impl<Hd, Tl, T, N> CoprodUninjector<T, There<N>> for Coproduct<Hd, Tl>
1122where
1123 Tl: CoprodUninjector<T, N>,
1124{
1125 type Remainder = Coproduct<Hd, Tl::Remainder>;
1126
1127 fn uninject(self) -> Result<T, Self::Remainder> {
1128 match self {
1129 Coproduct::Inl(h) => Err(Coproduct::Inl(h)),
1130 Coproduct::Inr(t) => t.uninject().map_err(Coproduct::Inr),
1131 }
1132 }
1133}
1134
1135/// Trait for extracting a subset of the possible types in a coproduct.
1136///
1137/// This trait is part of the implementation of the inherent method
1138/// [`Coproduct::subset`]. Please see that method for more information.
1139///
1140/// You only need to import this trait when working with generic
1141/// Coproducts of unknown type. If you have a Coproduct of known type,
1142/// then `co.subset()` should "just work" even without the trait.
1143///
1144/// [`Coproduct::subset`]: enum.Coproduct.html#method.subset
1145pub trait CoproductSubsetter<Targets, Indices>: Sized {
1146 type Remainder;
1147
1148 /// Extract a subset of the possible types in a coproduct (or get the remaining possibilities)
1149 ///
1150 /// Please see the [inherent method] for more information.
1151 ///
1152 /// The only difference between that inherent method and this
1153 /// trait method is the location of the type parameters.
1154 /// (here, they are on the trait rather than the method)
1155 ///
1156 /// [inherent method]: enum.Coproduct.html#method.subset
1157 fn subset(self) -> Result<Targets, Self::Remainder>;
1158}
1159
1160impl<Choices, THead, TTail, NHead, NTail, Rem>
1161 CoproductSubsetter<Coproduct<THead, TTail>, HCons<NHead, NTail>> for Choices
1162where
1163 Self: CoprodUninjector<THead, NHead, Remainder = Rem>,
1164 Rem: CoproductSubsetter<TTail, NTail>,
1165{
1166 type Remainder = <Rem as CoproductSubsetter<TTail, NTail>>::Remainder;
1167
1168 /// Attempt to extract a value from a subset of the types.
1169 fn subset(self) -> Result<Coproduct<THead, TTail>, Self::Remainder> {
1170 match self.uninject() {
1171 Ok(good) => Ok(Coproduct::Inl(good)),
1172 Err(bads) => match bads.subset() {
1173 Ok(goods) => Ok(Coproduct::Inr(goods)),
1174 Err(bads) => Err(bads),
1175 },
1176 }
1177 }
1178}
1179
1180impl<Choices> CoproductSubsetter<CNil, HNil> for Choices {
1181 type Remainder = Self;
1182
1183 #[inline(always)]
1184 fn subset(self) -> Result<CNil, Self::Remainder> {
1185 Err(self)
1186 }
1187}
1188
1189/// Trait for converting a coproduct into another that can hold its variants.
1190///
1191/// This trait is part of the implementation of the inherent method
1192/// [`Coproduct::embed`]. Please see that method for more information.
1193///
1194/// You only need to import this trait when working with generic
1195/// Coproducts of unknown type. If you have a Coproduct of known type,
1196/// then `co.embed()` should "just work" even without the trait.
1197///
1198/// [`Coproduct::embed`]: enum.Coproduct.html#method.embed
1199pub trait CoproductEmbedder<Out, Indices> {
1200 /// Convert a coproduct into another that can hold its variants.
1201 ///
1202 /// Please see the [inherent method] for more information.
1203 ///
1204 /// The only difference between that inherent method and this
1205 /// trait method is the location of the type parameters.
1206 /// (here, they are on the trait rather than the method)
1207 ///
1208 /// [inherent method]: enum.Coproduct.html#method.embed
1209 fn embed(self) -> Out;
1210}
1211
1212impl CoproductEmbedder<CNil, HNil> for CNil {
1213 fn embed(self) -> CNil {
1214 match self {
1215 // impossible!
1216 }
1217 }
1218}
1219
1220impl<Head, Tail> CoproductEmbedder<Coproduct<Head, Tail>, HNil> for CNil
1221where
1222 CNil: CoproductEmbedder<Tail, HNil>,
1223{
1224 fn embed(self) -> Coproduct<Head, Tail> {
1225 match self {
1226 // impossible!
1227 }
1228 }
1229}
1230
1231impl<Head, Tail, Out, NHead, NTail> CoproductEmbedder<Out, HCons<NHead, NTail>>
1232 for Coproduct<Head, Tail>
1233where
1234 Out: CoprodInjector<Head, NHead>,
1235 Tail: CoproductEmbedder<Out, NTail>,
1236{
1237 fn embed(self) -> Out {
1238 match self {
1239 Coproduct::Inl(this) => Out::inject(this),
1240 Coproduct::Inr(those) => those.embed(),
1241 }
1242 }
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247 use super::Coproduct::*;
1248 use super::*;
1249
1250 use std::format;
1251 use std::string::{String, ToString};
1252
1253 #[test]
1254 fn test_coproduct_inject() {
1255 type I32StrBool = Coprod!(i32, &'static str, bool);
1256
1257 let co1 = I32StrBool::inject(3);
1258 assert_eq!(co1, Inl(3));
1259 let get_from_1a: Option<&i32> = co1.get();
1260 let get_from_1b: Option<&bool> = co1.get();
1261 assert_eq!(get_from_1a, Some(&3));
1262 assert_eq!(get_from_1b, None);
1263
1264 let co2 = I32StrBool::inject(false);
1265 assert_eq!(co2, Inr(Inr(Inl(false))));
1266 let get_from_2a: Option<&i32> = co2.get();
1267 let get_from_2b: Option<&bool> = co2.get();
1268 assert_eq!(get_from_2a, None);
1269 assert_eq!(get_from_2b, Some(&false));
1270 }
1271
1272 #[test]
1273 fn test_coproduct_fold_consuming() {
1274 type I32F32StrBool = Coprod!(i32, f32, bool);
1275
1276 let co1 = I32F32StrBool::inject(3);
1277 let folded = co1.fold(hlist![
1278 |i| format!("int {}", i),
1279 |f| format!("float {}", f),
1280 |b| (if b { "t" } else { "f" }).to_string(),
1281 ]);
1282
1283 assert_eq!(folded, "int 3".to_string());
1284 }
1285
1286 #[test]
1287 fn test_coproduct_poly_fold_consuming() {
1288 type I32F32StrBool = Coprod!(i32, f32, bool);
1289
1290 impl Func<i32> for P {
1291 type Output = bool;
1292 fn call(args: i32) -> Self::Output {
1293 args > 100
1294 }
1295 }
1296 impl Func<bool> for P {
1297 type Output = bool;
1298 fn call(args: bool) -> Self::Output {
1299 args
1300 }
1301 }
1302 impl Func<f32> for P {
1303 type Output = bool;
1304 fn call(args: f32) -> Self::Output {
1305 args > 9000f32
1306 }
1307 }
1308 struct P;
1309
1310 let co1 = I32F32StrBool::inject(3);
1311 let folded = co1.fold(Poly(P));
1312
1313 assert!(!folded);
1314 }
1315
1316 #[test]
1317 fn test_coproduct_fold_non_consuming() {
1318 type I32F32Bool = Coprod!(i32, f32, bool);
1319
1320 let co1 = I32F32Bool::inject(3);
1321 let co2 = I32F32Bool::inject(true);
1322 let co3 = I32F32Bool::inject(42f32);
1323
1324 assert_eq!(
1325 co1.to_ref().fold(hlist![
1326 |&i| format!("int {}", i),
1327 |&f| format!("float {}", f),
1328 |&b| (if b { "t" } else { "f" }).to_string(),
1329 ]),
1330 "int 3".to_string()
1331 );
1332 assert_eq!(
1333 co2.to_ref().fold(hlist![
1334 |&i| format!("int {}", i),
1335 |&f| format!("float {}", f),
1336 |&b| (if b { "t" } else { "f" }).to_string(),
1337 ]),
1338 "t".to_string()
1339 );
1340 assert_eq!(
1341 co3.to_ref().fold(hlist![
1342 |&i| format!("int {}", i),
1343 |&f| format!("float {}", f),
1344 |&b| (if b { "t" } else { "f" }).to_string(),
1345 ]),
1346 "float 42".to_string()
1347 );
1348 }
1349
1350 #[test]
1351 fn test_coproduct_uninject() {
1352 type I32StrBool = Coprod!(i32, &'static str, bool);
1353
1354 let co1 = I32StrBool::inject(3);
1355 let co2 = I32StrBool::inject("hello");
1356 let co3 = I32StrBool::inject(false);
1357
1358 let uninject_i32_co1: Result<i32, _> = co1.uninject();
1359 let uninject_str_co1: Result<&'static str, _> = co1.uninject();
1360 let uninject_bool_co1: Result<bool, _> = co1.uninject();
1361 assert_eq!(uninject_i32_co1, Ok(3));
1362 assert!(uninject_str_co1.is_err());
1363 assert!(uninject_bool_co1.is_err());
1364
1365 let uninject_i32_co2: Result<i32, _> = co2.uninject();
1366 let uninject_str_co2: Result<&'static str, _> = co2.uninject();
1367 let uninject_bool_co2: Result<bool, _> = co2.uninject();
1368 assert!(uninject_i32_co2.is_err());
1369 assert_eq!(uninject_str_co2, Ok("hello"));
1370 assert!(uninject_bool_co2.is_err());
1371
1372 let uninject_i32_co3: Result<i32, _> = co3.uninject();
1373 let uninject_str_co3: Result<&'static str, _> = co3.uninject();
1374 let uninject_bool_co3: Result<bool, _> = co3.uninject();
1375 assert!(uninject_i32_co3.is_err());
1376 assert!(uninject_str_co3.is_err());
1377 assert_eq!(uninject_bool_co3, Ok(false));
1378 }
1379
1380 #[test]
1381 fn test_coproduct_subset() {
1382 type I32StrBool = Coprod!(i32, &'static str, bool);
1383
1384 // CNil can be extracted from anything.
1385 let res: Result<CNil, _> = I32StrBool::inject(3).subset();
1386 assert!(res.is_err());
1387
1388 if false {
1389 #[allow(unreachable_code, clippy::diverging_sub_expression)]
1390 {
1391 // ...including CNil.
1392 #[allow(unused)]
1393 let cnil: CNil = panic!();
1394 let _res: Result<CNil, _> = cnil.subset();
1395 let _ = res;
1396 }
1397 }
1398
1399 {
1400 // Order does not matter.
1401 let co = I32StrBool::inject(3);
1402 let res: Result<Coprod!(bool, i32), _> = co.subset();
1403 assert_eq!(res, Ok(Coproduct::Inr(Coproduct::Inl(3))));
1404
1405 let co = I32StrBool::inject("4");
1406 let res: Result<Coprod!(bool, i32), _> = co.subset();
1407 assert_eq!(res, Err(Coproduct::Inl("4")));
1408 }
1409 }
1410
1411 #[test]
1412 fn test_coproduct_embed() {
1413 // CNil can be embedded into any coproduct.
1414 if false {
1415 #[allow(unreachable_code, clippy::diverging_sub_expression)]
1416 {
1417 #[allow(unused)]
1418 let cnil: CNil = panic!();
1419 let _: CNil = cnil.embed();
1420
1421 #[allow(unused)]
1422 let cnil: CNil = panic!();
1423 let _: Coprod!(i32, bool) = cnil.embed();
1424 }
1425 }
1426
1427 #[derive(Debug, PartialEq)]
1428 struct A;
1429 #[derive(Debug, PartialEq)]
1430 struct B;
1431 #[derive(Debug, PartialEq)]
1432 struct C;
1433
1434 {
1435 // Order does not matter.
1436 let co_a = <Coprod!(C, A, B)>::inject(A);
1437 let co_b = <Coprod!(C, A, B)>::inject(B);
1438 let co_c = <Coprod!(C, A, B)>::inject(C);
1439 let out_a: Coprod!(A, B, C) = co_a.embed();
1440 let out_b: Coprod!(A, B, C) = co_b.embed();
1441 let out_c: Coprod!(A, B, C) = co_c.embed();
1442 assert_eq!(out_a, Coproduct::Inl(A));
1443 assert_eq!(out_b, Coproduct::Inr(Coproduct::Inl(B)));
1444 assert_eq!(out_c, Coproduct::Inr(Coproduct::Inr(Coproduct::Inl(C))));
1445 }
1446
1447 #[allow(clippy::upper_case_acronyms)]
1448 {
1449 // Multiple variants can resolve to the same output w/o type annotations
1450 type ABC = Coprod!(A, B, C);
1451 type BBB = Coprod!(B, B, B);
1452
1453 let b1 = BBB::inject::<_, Here>(B);
1454 let b2 = BBB::inject::<_, There<Here>>(B);
1455 let out1: ABC = b1.embed();
1456 let out2: ABC = b2.embed();
1457 assert_eq!(out1, Coproduct::Inr(Coproduct::Inl(B)));
1458 assert_eq!(out2, Coproduct::Inr(Coproduct::Inl(B)));
1459 }
1460 }
1461
1462 #[test]
1463 fn test_coproduct_map_ref() {
1464 type I32Bool = Coprod!(i32, bool);
1465 type I32BoolRef<'a> = Coprod!(i32, &'a bool);
1466
1467 fn map_it(co: &I32Bool) -> I32BoolRef<'_> {
1468 // For some reason rustc complains about lifetimes if you try to
1469 // inline the closure literal into the hlist ๐คท.
1470 let map_bool: fn(&bool) -> &bool = |b| b;
1471
1472 let mapper = hlist![|n: &i32| *n + 3, map_bool];
1473
1474 co.to_ref().map(mapper)
1475 }
1476
1477 let co = I32Bool::inject(3);
1478 let new = map_it(&co);
1479 assert_eq!(new, I32BoolRef::inject(6))
1480 }
1481
1482 #[test]
1483 fn test_coproduct_map_with_ref_mapper() {
1484 type I32Bool = Coprod!(i32, bool);
1485
1486 // HList mapper
1487
1488 let mapper = hlist![|n| n + 3, |b: bool| !b];
1489
1490 let co = I32Bool::inject(3);
1491 let co = co.map(&mapper);
1492 let co = co.map(&mapper);
1493
1494 assert_eq!(co, I32Bool::inject(9));
1495
1496 // Poly mapper
1497
1498 let mapper = poly_fn!(|n: i32| -> i32 { n + 3 }, |b: bool| -> bool { !b });
1499
1500 let co = I32Bool::inject(3);
1501 let co = co.map(&mapper);
1502 let co = co.map(&mapper);
1503
1504 assert_eq!(co, I32Bool::inject(9));
1505
1506 // Fn mapper
1507
1508 type StrStr = Coprod!(String, String);
1509
1510 let captured = String::from("!");
1511 let mapper = |s: String| format!("{}{}", s, &captured);
1512
1513 let co = StrStr::Inl(String::from("hi"));
1514 let co = co.map(&mapper);
1515 let co = co.map(&mapper);
1516
1517 assert_eq!(co, StrStr::Inl(String::from("hi!!")));
1518 }
1519
1520 #[test]
1521 fn test_coproduct_map_with_mut_mapper() {
1522 type I32Bool = Coprod!(i32, bool);
1523
1524 // HList mapper
1525
1526 let mut number = None;
1527 let mut boolean = None;
1528
1529 let mut mapper = hlist![
1530 |n: i32| {
1531 number = Some(n);
1532 n
1533 },
1534 |b: bool| {
1535 boolean = Some(b);
1536 b
1537 },
1538 ];
1539
1540 let co = I32Bool::inject(3);
1541 let co = co.map(&mut mapper);
1542 assert_eq!(co, I32Bool::inject(3));
1543 assert_eq!(number, Some(3));
1544 assert_eq!(boolean, None);
1545
1546 // Poly mapper
1547
1548 let mut mapper = poly_fn!(
1549 |n: i32| -> i32 {
1550 // Poly doesn't support capturing values.
1551 /* number = Some(n); */
1552 n
1553 },
1554 |b: bool| -> bool {
1555 // Poly doesn't support capturing values.
1556 /* boolean = Some(b) */
1557 b
1558 },
1559 );
1560
1561 let co = I32Bool::inject(3);
1562 let co = co.map(&mut mapper);
1563 assert_eq!(co, I32Bool::inject(3));
1564
1565 // Fn mapper
1566
1567 type StrStr = Coprod!(String, String);
1568
1569 let mut captured = String::new();
1570 let mut mapper = |s: String| {
1571 let s = format!("{s}!");
1572 captured.push_str(&s);
1573 s
1574 };
1575
1576 let co = StrStr::Inl(String::from("hi"));
1577 let co = co.map(&mut mapper);
1578 let co = co.map(&mut mapper);
1579
1580 assert_eq!(co, StrStr::Inl(String::from("hi!!")));
1581 assert_eq!(captured, String::from("hi!hi!!"));
1582 }
1583}