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	#[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}