Skip to main content

fp_library/types/optics/
exchange.rs

1//! The `Exchange` profunctor, used for isomorphisms.
2//!
3//! `Exchange<A, B, S, T>` wraps a forward function `S -> A` and a backward function `B -> T`.
4
5#[fp_macros::document_module]
6mod inner {
7	use {
8		crate::{
9			Apply,
10			brands::optics::*,
11			classes::{
12				CloneableFn,
13				Profunctor,
14			},
15			impl_kind,
16			kinds::*,
17		},
18		fp_macros::*,
19	};
20
21	/// The `Exchange` profunctor.
22	#[document_type_parameters(
23		"The lifetime of the functions.",
24		"The cloneable function brand.",
25		"The type of the value produced by the forward function.",
26		"The type of the value consumed by the backward function.",
27		"The source type of the structure.",
28		"The target type of the structure."
29	)]
30	pub struct Exchange<'a, FunctionBrand: CloneableFn, A: 'a, B: 'a, S: 'a, T: 'a> {
31		/// Forward function.
32		pub get: <FunctionBrand as CloneableFn>::Of<'a, S, A>,
33		/// Backward function.
34		pub set: <FunctionBrand as CloneableFn>::Of<'a, B, T>,
35	}
36
37	#[document_type_parameters(
38		"The lifetime of the functions.",
39		"The cloneable function brand.",
40		"The type of the value produced by the forward function.",
41		"The type of the value consumed by the backward function.",
42		"The source type of the structure.",
43		"The target type of the structure."
44	)]
45	impl<'a, FunctionBrand: CloneableFn, A: 'a, B: 'a, S: 'a, T: 'a>
46		Exchange<'a, FunctionBrand, A, B, S, T>
47	{
48		/// Creates a new `Exchange` instance.
49		#[document_signature]
50		///
51		#[document_parameters("The forward function.", "The backward function.")]
52		///
53		#[document_returns("A new instance of the type.")]
54		///
55		#[document_examples]
56		///
57		/// ```
58		/// use fp_library::{
59		/// 	brands::RcFnBrand,
60		/// 	classes::cloneable_fn::new as cloneable_fn_new,
61		/// 	types::optics::Exchange,
62		/// };
63		///
64		/// let exchange = Exchange::<RcFnBrand, _, _, _, _>::new(
65		/// 	cloneable_fn_new::<RcFnBrand, _, _>(|s: String| s.len()),
66		/// 	cloneable_fn_new::<RcFnBrand, _, _>(|n: usize| n.to_string()),
67		/// );
68		/// assert_eq!((exchange.get)("hello".to_string()), 5);
69		/// assert_eq!((exchange.set)(10), "10".to_string());
70		/// ```
71		pub fn new(
72			get: <FunctionBrand as CloneableFn>::Of<'a, S, A>,
73			set: <FunctionBrand as CloneableFn>::Of<'a, B, T>,
74		) -> Self {
75			Exchange {
76				get,
77				set,
78			}
79		}
80	}
81
82	impl_kind! {
83		impl<FunctionBrand: CloneableFn + 'static, A: 'static, B: 'static> for ExchangeBrand<FunctionBrand, A, B> {
84			#[document_default]
85			type Of<'a, S: 'a, T: 'a>: 'a = Exchange<'a, FunctionBrand, A, B, S, T>;
86		}
87	}
88
89	#[document_type_parameters(
90		"The cloneable function brand.",
91		"The type of the value produced by the forward function.",
92		"The type of the value consumed by the backward function."
93	)]
94	impl<FunctionBrand: CloneableFn + 'static, A: 'static, B: 'static> Profunctor
95		for ExchangeBrand<FunctionBrand, A, B>
96	{
97		/// Maps functions over the input and output of the `Exchange` profunctor.
98		#[document_signature]
99		#[document_type_parameters(
100			"The lifetime of the functions.",
101			"The source type of the new structure.",
102			"The target type of the new structure.",
103			"The source type of the original structure.",
104			"The target type of the original structure."
105		)]
106		///
107		#[document_parameters(
108			"The function to apply to the input.",
109			"The function to apply to the output.",
110			"The exchange instance to transform."
111		)]
112		#[document_returns("A transformed `Exchange` instance.")]
113		///
114		#[document_examples]
115		///
116		/// ```
117		/// use fp_library::{
118		/// 	brands::{
119		/// 		optics::*,
120		/// 		*,
121		/// 	},
122		/// 	classes::{
123		/// 		optics::*,
124		/// 		profunctor::*,
125		/// 		*,
126		/// 	},
127		/// 	functions::*,
128		/// 	types::optics::*,
129		/// };
130		///
131		/// let exchange: Exchange<RcFnBrand, usize, usize, String, String> =
132		/// 	Exchange::<RcFnBrand, _, _, _, _>::new(
133		/// 		cloneable_fn_new::<RcFnBrand, _, _>(|s: String| s.len()),
134		/// 		cloneable_fn_new::<RcFnBrand, _, _>(|n: usize| n.to_string()),
135		/// 	);
136		///
137		/// let transformed = <ExchangeBrand<RcFnBrand, usize, usize> as Profunctor>::dimap(
138		/// 	|s: &str| s.to_string(),
139		/// 	|s: String| s.len(),
140		/// 	exchange,
141		/// );
142		/// assert_eq!((transformed.get)("hello"), 5);
143		/// ```
144		fn dimap<'a, S: 'a, T: 'a, U: 'a, V: 'a>(
145			st: impl Fn(S) -> T + 'a,
146			uv: impl Fn(U) -> V + 'a,
147			puv: Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, T, U>),
148		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, S, V>) {
149			let get = puv.get;
150			let set = puv.set;
151			let st = <FunctionBrand as CloneableFn>::new(st);
152			let uv = <FunctionBrand as CloneableFn>::new(uv);
153			Exchange::new(
154				<FunctionBrand as CloneableFn>::new(move |s: S| (*get)((*st)(s))),
155				<FunctionBrand as CloneableFn>::new(move |b: B| (*uv)((*set)(b))),
156			)
157		}
158	}
159}
160pub use inner::*;