Skip to main content

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	#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43	#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
44	pub struct Dual<A>(
45		/// The wrapped value.
46		pub A,
47	);
48
49	#[document_type_parameters("The semigroup type.")]
50	impl<A: Semigroup> Semigroup for Dual<A> {
51		/// Combines two values in reverse order.
52		///
53		/// `append(Dual(a), Dual(b))` computes `Dual(append(b, a))`.
54		#[document_signature]
55		///
56		#[document_parameters("The first dual value.", "The second dual value.")]
57		///
58		#[document_returns("The reversed combination wrapped in `Dual`.")]
59		#[document_examples]
60		///
61		/// ```
62		/// use fp_library::{
63		/// 	functions::*,
64		/// 	types::Dual,
65		/// };
66		///
67		/// assert_eq!(append(Dual("ab".to_string()), Dual("cd".to_string())), Dual("cdab".to_string()));
68		/// ```
69		fn append(
70			a: Self,
71			b: Self,
72		) -> Self {
73			Dual(A::append(b.0, a.0))
74		}
75	}
76
77	#[document_type_parameters("The monoid type.")]
78	impl<A: Monoid> Monoid for Dual<A> {
79		/// Returns `Dual(empty())`.
80		#[document_signature]
81		///
82		#[document_returns("The identity element wrapped in `Dual`.")]
83		#[document_examples]
84		///
85		/// ```
86		/// use fp_library::{
87		/// 	functions::*,
88		/// 	types::Dual,
89		/// };
90		///
91		/// assert_eq!(empty::<Dual<String>>(), Dual(String::new()));
92		/// ```
93		fn empty() -> Self {
94			Dual(A::empty())
95		}
96	}
97}
98
99pub use inner::*;
100
101#[cfg(test)]
102mod tests {
103	use {
104		super::*,
105		crate::functions::*,
106		quickcheck_macros::quickcheck,
107	};
108
109	#[quickcheck]
110	fn semigroup_associativity(
111		a: String,
112		b: String,
113		c: String,
114	) -> bool {
115		let x = Dual(a);
116		let y = Dual(b);
117		let z = Dual(c);
118		append(x.clone(), append(y.clone(), z.clone())) == append(append(x, y), z)
119	}
120
121	#[quickcheck]
122	fn monoid_left_identity(a: String) -> bool {
123		let x = Dual(a);
124		append(empty::<Dual<String>>(), x.clone()) == x
125	}
126
127	#[quickcheck]
128	fn monoid_right_identity(a: String) -> bool {
129		let x = Dual(a);
130		append(x.clone(), empty::<Dual<String>>()) == x
131	}
132}