fp_library/types/dual.rs
1//! A newtype wrapper that reverses the order of a [`Semigroup`](crate::classes::Semigroup)'s operation.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! functions::*,
8//! types::Dual,
9//! };
10//!
11//! let x = Dual("hello ".to_string());
12//! let y = Dual("world".to_string());
13//! // Dual reverses append order: append(Dual(a), Dual(b)) = Dual(append(b, a))
14//! assert_eq!(append(x, y), Dual("worldhello ".to_string()));
15//! ```
16
17#[fp_macros::document_module]
18mod inner {
19 use {
20 crate::classes::*,
21 fp_macros::*,
22 };
23
24 /// A newtype wrapper that reverses the order of a [`Semigroup`]'s operation.
25 ///
26 /// `append(Dual(a), Dual(b))` is equivalent to `Dual(append(b, a))`.
27 ///
28 /// If the inner type is a [`Monoid`], `Dual` is also a [`Monoid`] with the
29 /// same identity element.
30 #[document_examples]
31 ///
32 /// ```
33 /// use fp_library::{
34 /// functions::*,
35 /// types::Dual,
36 /// };
37 ///
38 /// let x = Dual("ab".to_string());
39 /// let y = Dual("cd".to_string());
40 /// assert_eq!(append(x, y), Dual("cdab".to_string()));
41 /// ```
42 #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
43 pub struct Dual<A>(
44 /// The wrapped value.
45 pub A,
46 );
47
48 #[document_type_parameters("The semigroup type.")]
49 impl<A: Semigroup> Semigroup for Dual<A> {
50 /// Combines two values in reverse order.
51 ///
52 /// `append(Dual(a), Dual(b))` computes `Dual(append(b, a))`.
53 #[document_signature]
54 ///
55 #[document_parameters("The first dual value.", "The second dual value.")]
56 ///
57 #[document_returns("The reversed combination wrapped in `Dual`.")]
58 #[document_examples]
59 ///
60 /// ```
61 /// use fp_library::{
62 /// functions::*,
63 /// types::Dual,
64 /// };
65 ///
66 /// assert_eq!(append(Dual("ab".to_string()), Dual("cd".to_string())), Dual("cdab".to_string()));
67 /// ```
68 fn append(
69 a: Self,
70 b: Self,
71 ) -> Self {
72 Dual(A::append(b.0, a.0))
73 }
74 }
75
76 #[document_type_parameters("The monoid type.")]
77 impl<A: Monoid> Monoid for Dual<A> {
78 /// Returns `Dual(empty())`.
79 #[document_signature]
80 ///
81 #[document_returns("The identity element wrapped in `Dual`.")]
82 #[document_examples]
83 ///
84 /// ```
85 /// use fp_library::{
86 /// functions::*,
87 /// types::Dual,
88 /// };
89 ///
90 /// assert_eq!(empty::<Dual<String>>(), Dual(String::new()));
91 /// ```
92 fn empty() -> Self {
93 Dual(A::empty())
94 }
95 }
96}
97
98pub use inner::*;
99
100#[cfg(test)]
101mod tests {
102 use {
103 super::*,
104 crate::functions::*,
105 quickcheck_macros::quickcheck,
106 };
107
108 #[quickcheck]
109 fn semigroup_associativity(
110 a: String,
111 b: String,
112 c: String,
113 ) -> bool {
114 let x = Dual(a);
115 let y = Dual(b);
116 let z = Dual(c);
117 append(x.clone(), append(y.clone(), z.clone())) == append(append(x, y), z)
118 }
119
120 #[quickcheck]
121 fn monoid_left_identity(a: String) -> bool {
122 let x = Dual(a);
123 append(empty::<Dual<String>>(), x.clone()) == x
124 }
125
126 #[quickcheck]
127 fn monoid_right_identity(a: String) -> bool {
128 let x = Dual(a);
129 append(x.clone(), empty::<Dual<String>>()) == x
130 }
131}