Skip to main content

fp_library/types/optics/
indexed.rs

1//! The `Indexed` profunctor wrapper.
2//!
3//! `Indexed<'a, P, I, A, B>` wraps a profunctor `P` to carry an index `I` alongside the focus `A`.
4
5#[fp_macros::document_module]
6mod inner {
7	use {
8		crate::{
9			Apply,
10			brands::optics::*,
11			classes::{
12				applicative::Applicative,
13				profunctor::{
14					Choice,
15					Profunctor,
16					Strong,
17					Wander,
18					wander::TraversalFunc,
19				},
20			},
21			impl_kind,
22			kinds::*,
23		},
24		fp_macros::*,
25		std::marker::PhantomData,
26	};
27
28	/// The `Indexed` profunctor wrapper.
29	///
30	/// `Indexed<'a, P, I, A, B>` wraps a profunctor `P` that operates on `(I, A)` and `B`.
31	#[document_type_parameters(
32		"The lifetime of the values.",
33		"The underlying profunctor brand.",
34		"The index type.",
35		"The focus type.",
36		"The target focus type."
37	)]
38	pub struct Indexed<'a, P, I, A, B>
39	where
40		P: Profunctor,
41		I: 'a,
42		A: 'a,
43		B: 'a, {
44		/// The underlying profunctor value.
45		pub inner: Apply!(<P as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, (I, A), B>),
46	}
47
48	#[document_type_parameters(
49		"The lifetime of the values.",
50		"The underlying profunctor brand.",
51		"The index type.",
52		"The focus type.",
53		"The target focus type."
54	)]
55	impl<'a, P, I, A, B> Indexed<'a, P, I, A, B>
56	where
57		P: Profunctor,
58		I: 'a,
59		A: 'a,
60		B: 'a,
61	{
62		/// Creates a new `Indexed` instance.
63		#[document_signature]
64		#[document_parameters("The underlying profunctor value.")]
65		#[document_returns("A new `Indexed` instance.")]
66		#[document_examples]
67		///
68		/// ```
69		/// use fp_library::{
70		/// 	brands::RcFnBrand,
71		/// 	types::optics::Indexed,
72		/// };
73		/// let f = |(i, a): (usize, i32)| a + (i as i32);
74		/// let indexed = Indexed::<RcFnBrand, usize, i32, i32>::new(std::rc::Rc::new(f));
75		/// assert_eq!((indexed.inner)((10, 32)), 42);
76		/// ```
77		pub fn new(
78			inner: Apply!(<P as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, (I, A), B>)
79		) -> Self {
80			Self {
81				inner,
82			}
83		}
84	}
85
86	impl_kind! {
87		impl<P: Profunctor + 'static, I: 'static> for IndexedBrand<P, I> {
88			#[document_default]
89			type Of<'a, A: 'a, B: 'a>: 'a = Indexed<'a, P, I, A, B>;
90		}
91	}
92
93	#[document_type_parameters("The underlying profunctor brand.", "The index type.")]
94	impl<P: Profunctor + 'static, I: 'static> Profunctor for IndexedBrand<P, I> {
95		#[document_signature]
96		#[document_type_parameters(
97			"The lifetime of the values.",
98			"The new input type.",
99			"The original input type.",
100			"The original output type.",
101			"The new output type."
102		)]
103		#[document_parameters(
104			"The contravariant function to apply to the input.",
105			"The covariant function to apply to the output.",
106			"The indexed profunctor instance."
107		)]
108		#[document_returns("A transformed `Indexed` instance.")]
109		#[document_examples]
110		///
111		/// ```
112		/// use fp_library::{
113		/// 	brands::{
114		/// 		RcFnBrand,
115		/// 		optics::*,
116		/// 	},
117		/// 	classes::profunctor::*,
118		/// 	types::optics::Indexed,
119		/// };
120		/// let f = |(i, a): (usize, i32)| a + (i as i32);
121		/// let indexed = Indexed::<RcFnBrand, usize, i32, i32>::new(std::rc::Rc::new(f));
122		/// let transformed = <IndexedBrand<RcFnBrand, usize> as Profunctor>::dimap(
123		/// 	|a: i32| a * 2,
124		/// 	|b: i32| b - 1,
125		/// 	indexed,
126		/// );
127		/// assert_eq!((transformed.inner)((10, 16)), 41); // (10 + (16 * 2)) - 1 = 41
128		/// ```
129		fn dimap<'a, A: 'a, B: 'a, C: 'a, D: 'a>(
130			ab: impl Fn(A) -> B + 'a,
131			cd: impl Fn(C) -> D + 'a,
132			pbc: Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, B, C>),
133		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, A, D>) {
134			Indexed::new(P::dimap(move |(i, a)| (i, ab(a)), cd, pbc.inner))
135		}
136	}
137
138	#[document_type_parameters("The underlying profunctor brand.", "The index type.")]
139	impl<P: Strong + 'static, I: 'static> Strong for IndexedBrand<P, I> {
140		#[document_signature]
141		#[document_type_parameters(
142			"The lifetime of the values.",
143			"The input type of the profunctor.",
144			"The output type of the profunctor.",
145			"The type of the second component (threaded through unchanged)."
146		)]
147		#[document_parameters("The indexed profunctor instance to lift.")]
148		#[document_returns("A transformed `Indexed` instance that operates on pairs.")]
149		#[document_examples]
150		///
151		/// ```
152		/// use fp_library::{
153		/// 	brands::RcFnBrand,
154		/// 	classes::profunctor::*,
155		/// 	brands::optics::*,
156		/// 	types::optics::Indexed,
157		/// };
158		/// let f = |(i, a): (usize, i32)| a + (i as i32);
159		/// let indexed = Indexed::<RcFnBrand, usize, i32, i32>::new(std::rc::Rc::new(f));
160		/// let transformed = <IndexedBrand<RcFnBrand, usize> as Strong>::first::<i32, i32, i32>(indexed);
161		/// assert_eq!((transformed.inner)((10, (16, 100))), (26, 100)); // (10 + 16) = 26, 100 threaded through
162		/// ```
163		fn first<'a, A: 'a, B: 'a, C>(
164			pab: Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, A, B>)
165		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, (A, C), (B, C)>) {
166			Indexed::new(P::dimap(
167				|(i, (a, c))| ((i, a), c),
168				|(b, c)| (b, c),
169				P::first::<(I, A), B, C>(pab.inner),
170			))
171		}
172
173		#[document_signature]
174		#[document_type_parameters(
175			"The lifetime of the values.",
176			"The input type of the profunctor.",
177			"The output type of the profunctor.",
178			"The type of the first component (threaded through unchanged)."
179		)]
180		#[document_parameters("The indexed profunctor instance to lift.")]
181		#[document_returns("A transformed `Indexed` instance that operates on pairs.")]
182		#[document_examples]
183		///
184		/// ```
185		/// use fp_library::{
186		/// 	brands::RcFnBrand,
187		/// 	classes::profunctor::*,
188		/// 	brands::optics::*,
189		/// 	types::optics::Indexed,
190		/// };
191		/// let f = |(i, b): (usize, i32)| b + (i as i32);
192		/// let indexed = Indexed::<RcFnBrand, usize, i32, i32>::new(std::rc::Rc::new(f));
193		/// let transformed = <IndexedBrand<RcFnBrand, usize> as Strong>::second::<i32, i32, i32>(indexed);
194		/// assert_eq!((transformed.inner)((10, (100, 16))), (100, 26)); // (10 + 16) = 26, 100 threaded through
195		/// ```
196		fn second<'a, A: 'a, B: 'a, C: 'a>(
197			pab: Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, A, B>)
198		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, (C, A), (C, B)>) {
199			Indexed::new(P::dimap(
200				|(i, (c, a))| (c, (i, a)),
201				|(c, b)| (c, b),
202				P::second::<(I, A), B, C>(pab.inner),
203			))
204		}
205	}
206
207	#[document_type_parameters("The underlying profunctor brand.", "The index type.")]
208	impl<P: Choice + 'static, I: 'static> Choice for IndexedBrand<P, I> {
209		#[document_signature]
210		#[document_type_parameters(
211			"The lifetime of the values.",
212			"The input type of the profunctor.",
213			"The output type of the profunctor.",
214			"The type of the alternative variant (threaded through unchanged)."
215		)]
216		#[document_parameters("The indexed profunctor instance to lift.")]
217		#[document_returns("A transformed `Indexed` instance that operates on `Result` types.")]
218		#[document_examples]
219		///
220		/// ```
221		/// use fp_library::{
222		/// 	brands::{
223		/// 		RcFnBrand,
224		/// 		optics::*,
225		/// 	},
226		/// 	classes::profunctor::*,
227		/// 	types::optics::Indexed,
228		/// };
229		/// let f = |(i, a): (usize, i32)| a + (i as i32);
230		/// let indexed = Indexed::<RcFnBrand, usize, i32, i32>::new(std::rc::Rc::new(f));
231		/// let transformed = <IndexedBrand<RcFnBrand, usize> as Choice>::left::<i32, i32, i32>(indexed);
232		/// assert_eq!((transformed.inner)((10, Err(32))), Err(42));
233		/// ```
234		fn left<'a, A: 'a, B: 'a, C: 'a>(
235			pab: Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, A, B>)
236		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, Result<C, A>, Result<C, B>>)
237		{
238			Indexed::new(P::dimap(
239				|(i, r)| match r {
240					Err(a) => Err((i, a)),
241					Ok(c) => Ok(c),
242				},
243				|r| match r {
244					Err(b) => Err(b),
245					Ok(c) => Ok(c),
246				},
247				P::left::<(I, A), B, C>(pab.inner),
248			))
249		}
250
251		#[document_signature]
252		#[document_type_parameters(
253			"The lifetime of the values.",
254			"The input type of the profunctor.",
255			"The output type of the profunctor.",
256			"The type of the alternative variant (threaded through unchanged)."
257		)]
258		#[document_parameters("The indexed profunctor instance to lift.")]
259		#[document_returns("A transformed `Indexed` instance that operates on `Result` types.")]
260		#[document_examples]
261		///
262		/// ```
263		/// use fp_library::{
264		/// 	brands::{
265		/// 		RcFnBrand,
266		/// 		optics::*,
267		/// 	},
268		/// 	classes::profunctor::*,
269		/// 	types::optics::Indexed,
270		/// };
271		/// let f = |(i, b): (usize, i32)| b + (i as i32);
272		/// let indexed = Indexed::<RcFnBrand, usize, i32, i32>::new(std::rc::Rc::new(f));
273		/// let transformed = <IndexedBrand<RcFnBrand, usize> as Choice>::right::<i32, i32, i32>(indexed);
274		/// assert_eq!((transformed.inner)((10, Ok(32))), Ok(42));
275		/// ```
276		fn right<'a, A: 'a, B: 'a, C: 'a>(
277			pab: Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, A, B>)
278		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, Result<A, C>, Result<B, C>>)
279		{
280			Indexed::new(P::dimap(
281				|(i, r)| match r {
282					Ok(a) => Ok((i, a)),
283					Err(c) => Err(c),
284				},
285				|r| match r {
286					Ok(b) => Ok(b),
287					Err(c) => Err(c),
288				},
289				P::right::<(I, A), B, C>(pab.inner),
290			))
291		}
292	}
293
294	#[document_type_parameters("The underlying profunctor brand.", "The index type.")]
295	impl<P: Wander + 'static, I: Clone + 'static> Wander for IndexedBrand<P, I> {
296		#[document_signature]
297		#[document_type_parameters(
298			"The lifetime of the values.",
299			"The source type of the structure.",
300			"The target type of the structure.",
301			"The source type of the focus.",
302			"The target type of the focus."
303		)]
304		#[document_parameters("The traversal function.", "The indexed profunctor instance.")]
305		#[document_returns("A transformed `Indexed` instance that operates on structures.")]
306		#[document_examples]
307		///
308		/// ```
309		/// use fp_library::{
310		/// 	brands::{
311		/// 		RcBrand,
312		/// 		RcFnBrand,
313		/// 		VecBrand,
314		/// 		optics::*,
315		/// 	},
316		/// 	classes::{
317		/// 		optics::IndexedTraversalOptic,
318		/// 		profunctor::*,
319		/// 	},
320		/// 	functions::*,
321		/// 	types::optics::*,
322		/// };
323		/// // Use an indexed traversal over a Vec to demonstrate wandering with index
324		/// let traversal: IndexedTraversal<
325		/// 	RcBrand,
326		/// 	usize,
327		/// 	Vec<i32>,
328		/// 	Vec<i32>,
329		/// 	i32,
330		/// 	i32,
331		/// 	Traversed<VecBrand>,
332		/// > = IndexedTraversal::traversed();
333		/// let f = std::rc::Rc::new(|(i, x): (usize, i32)| x + (i as i32))
334		/// 	as std::rc::Rc<dyn Fn((usize, i32)) -> i32>;
335		/// let pab = Indexed::<RcFnBrand, _, _, _>::new(f);
336		/// let result: std::rc::Rc<dyn Fn(Vec<i32>) -> Vec<i32>> =
337		/// 	IndexedTraversalOptic::evaluate::<RcFnBrand>(&traversal, pab);
338		/// assert_eq!(result(vec![10, 20]), vec![10, 21]);
339		/// ```
340		fn wander<'a, S: 'a, T: 'a, A: 'a, B: 'a + Clone>(
341			traversal: impl TraversalFunc<'a, S, T, A, B> + 'a,
342			pab: Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, A, B>),
343		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a, U: 'a>: 'a; )>::Of<'a, S, T>) {
344			struct IWanderAdapter<'a, I, S, T, A, B, TFunc> {
345				traversal: TFunc,
346				_phantom: PhantomData<&'a (I, S, T, A, B)>,
347			}
348
349			impl<'a, I: Clone + 'a, S: 'a, T: 'a, A: 'a, B: 'a, TFunc>
350				TraversalFunc<'a, (I, S), T, (I, A), B> for IWanderAdapter<'a, I, S, T, A, B, TFunc>
351			where
352				TFunc: TraversalFunc<'a, S, T, A, B> + 'a,
353			{
354				fn apply<M: Applicative>(
355					&self,
356					f: Box<
357						dyn Fn((I, A)) -> Apply!(<M as Kind!( type Of<'c, U: 'c>: 'c; )>::Of<'a, B>)
358							+ 'a,
359					>,
360					(i, s): (I, S),
361				) -> Apply!(<M as Kind!( type Of<'c, U: 'c>: 'c; )>::Of<'a, T>)
362				where
363					Apply!(<M as Kind!( type Of<'c, U: 'c>: 'c; )>::Of<'a, B>): Clone, {
364					let i_clone = i.clone();
365					self.traversal.apply::<M>(Box::new(move |a| f((i_clone.clone(), a))), s)
366				}
367			}
368
369			Indexed::new(P::wander(
370				IWanderAdapter {
371					traversal,
372					_phantom: PhantomData,
373				},
374				pab.inner,
375			))
376		}
377	}
378}
379
380pub use inner::*;