Skip to main content

fp_library/types/optics/
traversal.rs

1//! Traversal optics for operating on multiple foci.
2//!
3//! A traversal represents a way to focus on zero or more values in a structure
4//! and update them while maintaining the structure.
5
6#[fp_macros::document_module]
7mod inner {
8	use {
9		crate::{
10			Apply,
11			brands::{
12				FnBrand,
13				optics::*,
14			},
15			classes::{
16				ToDynCloneFn,
17				monoid::Monoid,
18				optics::*,
19				profunctor::Wander,
20			},
21			kinds::*,
22		},
23		fp_macros::*,
24		std::marker::PhantomData,
25	};
26
27	/// A polymorphic traversal.
28	///
29	/// Matches PureScript's `Traversal s t a b`.
30	#[document_type_parameters(
31		"The lifetime of the values.",
32		"The reference-counted pointer type.",
33		"The source type of the structure.",
34		"The target type of the structure after an update.",
35		"The source type of the focus.",
36		"The target type of the focus after an update.",
37		"The type of the traversal function."
38	)]
39	pub struct Traversal<'a, PointerBrand, S, T, A, B, F>
40	where
41		PointerBrand: ToDynCloneFn,
42		F: TraversalFunc<'a, S, T, A, B> + 'a,
43		S: 'a,
44		T: 'a,
45		A: 'a,
46		B: 'a, {
47		/// The traversal function.
48		///
49		/// In PureScript this is `(forall f. Applicative f => (a -> f b) -> s -> f t)`.
50		pub traversal: F,
51		pub(crate) _phantom: PhantomData<(&'a (S, T, A, B), PointerBrand)>,
52	}
53
54	#[document_type_parameters(
55		"The lifetime of the values.",
56		"The reference-counted pointer type.",
57		"The source type of the structure.",
58		"The target type of the structure after an update.",
59		"The source type of the focus.",
60		"The target type of the focus after an update.",
61		"The type of the traversal function."
62	)]
63	impl<'a, PointerBrand, S, T, A, B, F> Traversal<'a, PointerBrand, S, T, A, B, F>
64	where
65		PointerBrand: ToDynCloneFn,
66		F: TraversalFunc<'a, S, T, A, B> + 'a,
67	{
68		/// Creates a new `Traversal` instance.
69		#[document_signature]
70		///
71		#[document_parameters("The traversal function.")]
72		///
73		#[document_returns("A new instance of the type.")]
74		///
75		#[document_examples]
76		///
77		/// ```
78		/// use fp_library::{
79		/// 	Apply,
80		/// 	brands::*,
81		/// 	classes::{
82		/// 		Applicative,
83		/// 		lift::Lift,
84		/// 		optics::traversal::TraversalFunc,
85		/// 		pointed::Pointed,
86		/// 	},
87		/// 	kinds::*,
88		/// 	types::optics::Traversal,
89		/// };
90		///
91		/// #[derive(Clone)]
92		/// struct ListTraversal;
93		/// impl<'a, A: 'a + Clone> TraversalFunc<'a, Vec<A>, Vec<A>, A, A> for ListTraversal {
94		/// 	fn apply<M: Applicative>(
95		/// 		&self,
96		/// 		f: Box<dyn Fn(A) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, A>) + 'a>,
97		/// 		s: Vec<A>,
98		/// 	) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, Vec<A>>) {
99		/// 		s.into_iter().fold(M::pure(vec![]), |acc, a| {
100		/// 			M::lift2(
101		/// 				|mut v: Vec<A>, x: A| {
102		/// 					v.push(x);
103		/// 					v
104		/// 				},
105		/// 				acc,
106		/// 				f(a),
107		/// 			)
108		/// 		})
109		/// 	}
110		/// }
111		///
112		/// let traversal = Traversal::<'_, RcBrand, Vec<i32>, Vec<i32>, i32, i32, _>::new(ListTraversal);
113		/// assert_eq!(
114		/// 	traversal.traversal.apply::<OptionBrand>(Box::new(|x| Some(x + 1)), vec![1, 2]),
115		/// 	Some(vec![2, 3])
116		/// );
117		/// ```
118		pub fn new(traversal: F) -> Self {
119			Traversal {
120				traversal,
121				_phantom: PhantomData,
122			}
123		}
124	}
125
126	/// A monomorphic traversal.
127	///
128	/// Matches PureScript's `Traversal' s a`.
129	#[document_type_parameters(
130		"The lifetime of the values.",
131		"The reference-counted pointer type.",
132		"The type of the structure.",
133		"The type of the focus.",
134		"The type of the traversal function."
135	)]
136	pub struct TraversalPrime<'a, PointerBrand, S, A, F>
137	where
138		PointerBrand: ToDynCloneFn,
139		F: TraversalFunc<'a, S, S, A, A> + 'a,
140		S: 'a,
141		A: 'a, {
142		/// The traversal function.
143		pub traversal: F,
144		pub(crate) _phantom: PhantomData<(&'a (S, A), PointerBrand)>,
145	}
146
147	#[document_type_parameters(
148		"The lifetime of the values.",
149		"The reference-counted pointer type.",
150		"The type of the structure.",
151		"The type of the focus.",
152		"The type of the traversal function."
153	)]
154	impl<'a, PointerBrand, S, A, F> TraversalPrime<'a, PointerBrand, S, A, F>
155	where
156		PointerBrand: ToDynCloneFn,
157		F: TraversalFunc<'a, S, S, A, A> + 'a,
158	{
159		/// Creates a new `TraversalPrime` instance.
160		#[document_signature]
161		///
162		#[document_parameters("The traversal function.")]
163		///
164		#[document_returns("A new instance of the type.")]
165		///
166		#[document_examples]
167		///
168		/// ```
169		/// use fp_library::{
170		/// 	Apply,
171		/// 	brands::*,
172		/// 	classes::{
173		/// 		Applicative,
174		/// 		lift::Lift,
175		/// 		optics::traversal::TraversalFunc,
176		/// 		pointed::Pointed,
177		/// 	},
178		/// 	kinds::*,
179		/// 	types::optics::TraversalPrime,
180		/// };
181		///
182		/// #[derive(Clone)]
183		/// struct ListTraversal;
184		/// impl<'a, A: 'a + Clone> TraversalFunc<'a, Vec<A>, Vec<A>, A, A> for ListTraversal {
185		/// 	fn apply<M: Applicative>(
186		/// 		&self,
187		/// 		f: Box<dyn Fn(A) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, A>) + 'a>,
188		/// 		s: Vec<A>,
189		/// 	) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, Vec<A>>) {
190		/// 		s.into_iter().fold(M::pure(vec![]), |acc, a| {
191		/// 			M::lift2(
192		/// 				|mut v: Vec<A>, x: A| {
193		/// 					v.push(x);
194		/// 					v
195		/// 				},
196		/// 				acc,
197		/// 				f(a),
198		/// 			)
199		/// 		})
200		/// 	}
201		/// }
202		///
203		/// let traversal = TraversalPrime::<'_, RcBrand, Vec<i32>, i32, _>::new(ListTraversal);
204		/// assert_eq!(
205		/// 	traversal.traversal.apply::<OptionBrand>(Box::new(|x| Some(x + 1)), vec![1, 2]),
206		/// 	Some(vec![2, 3])
207		/// );
208		/// ```
209		pub fn new(traversal: F) -> Self {
210			TraversalPrime {
211				traversal,
212				_phantom: PhantomData,
213			}
214		}
215	}
216
217	#[document_type_parameters(
218		"The lifetime of the values.",
219		"The profunctor type.",
220		"The reference-counted pointer type.",
221		"The source type of the structure.",
222		"The target type of the structure after an update.",
223		"The source type of the focus.",
224		"The target type of the focus after an update.",
225		"The type of the traversal function."
226	)]
227	#[document_parameters("The traversal instance.")]
228	impl<'a, Q, PointerBrand, S, T, A, B, F> Optic<'a, Q, S, T, A, B>
229		for Traversal<'a, PointerBrand, S, T, A, B, F>
230	where
231		Q: Wander,
232		PointerBrand: ToDynCloneFn,
233		F: TraversalFunc<'a, S, T, A, B> + Clone + 'a,
234		S: 'a,
235		T: 'a,
236		A: 'a,
237		B: 'a + Clone,
238	{
239		/// Evaluates the traversal with a profunctor.
240		#[document_signature]
241		///
242		#[document_parameters("The profunctor value to transform.")]
243		///
244		#[document_returns("The transformed profunctor value.")]
245		///
246		#[document_examples]
247		///
248		/// ```
249		/// use fp_library::{
250		/// 	Apply,
251		/// 	brands::*,
252		/// 	classes::{
253		/// 		Applicative,
254		/// 		lift::Lift,
255		/// 		optics::{
256		/// 			traversal::TraversalFunc,
257		/// 			*,
258		/// 		},
259		/// 		pointed::Pointed,
260		/// 		profunctor::*,
261		/// 	},
262		/// 	kinds::*,
263		/// 	types::optics::Traversal,
264		/// };
265		///
266		/// #[derive(Clone)]
267		/// struct ListTraversal;
268		/// impl<'a, A: 'a + Clone> TraversalFunc<'a, Vec<A>, Vec<A>, A, A> for ListTraversal {
269		/// 	fn apply<M: Applicative>(
270		/// 		&self,
271		/// 		f: Box<dyn Fn(A) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, A>) + 'a>,
272		/// 		s: Vec<A>,
273		/// 	) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, Vec<A>>) {
274		/// 		s.into_iter().fold(M::pure(vec![]), |acc, a| {
275		/// 			M::lift2(
276		/// 				|mut v: Vec<A>, x: A| {
277		/// 					v.push(x);
278		/// 					v
279		/// 				},
280		/// 				acc,
281		/// 				f(a),
282		/// 			)
283		/// 		})
284		/// 	}
285		/// }
286		///
287		/// let traversal = Traversal::<'_, RcBrand, Vec<i32>, Vec<i32>, i32, i32, _>::new(ListTraversal);
288		/// let f = std::rc::Rc::new(|x: i32| x + 1) as std::rc::Rc<dyn Fn(i32) -> i32>;
289		/// let result: std::rc::Rc<dyn Fn(Vec<i32>) -> Vec<i32>> =
290		/// 	Optic::<'_, RcFnBrand, _, _, _, _>::evaluate(&traversal, f);
291		/// assert_eq!(result(vec![1, 2]), vec![2, 3]);
292		/// ```
293		fn evaluate(
294			&self,
295			pab: Apply!(<Q as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, A, B>),
296		) -> Apply!(<Q as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, S, T>) {
297			Q::wander(self.traversal.clone(), pab)
298		}
299	}
300
301	#[document_type_parameters(
302		"The lifetime of the values.",
303		"The reference-counted pointer type.",
304		"The source type of the structure.",
305		"The target type of the structure after an update.",
306		"The source type of the focus.",
307		"The target type of the focus after an update.",
308		"The type of the traversal function."
309	)]
310	#[document_parameters("The traversal instance.")]
311	impl<'a, PointerBrand, S, T, A, B, F> TraversalOptic<'a, S, T, A, B>
312		for Traversal<'a, PointerBrand, S, T, A, B, F>
313	where
314		PointerBrand: ToDynCloneFn,
315		F: TraversalFunc<'a, S, T, A, B> + Clone + 'a,
316		S: 'a,
317		T: 'a,
318		A: 'a,
319		B: 'a + Clone,
320	{
321		#[document_signature]
322		#[document_type_parameters("The profunctor type.")]
323		#[document_parameters("The profunctor value to transform.")]
324		#[document_returns("The transformed profunctor value.")]
325		#[document_examples]
326		///
327		/// ```
328		/// use fp_library::{
329		/// 	Apply,
330		/// 	brands::*,
331		/// 	classes::{
332		/// 		Applicative,
333		/// 		lift::Lift,
334		/// 		optics::{
335		/// 			traversal::TraversalFunc,
336		/// 			*,
337		/// 		},
338		/// 		pointed::Pointed,
339		/// 		profunctor::*,
340		/// 	},
341		/// 	kinds::*,
342		/// 	types::optics::Traversal,
343		/// };
344		///
345		/// #[derive(Clone)]
346		/// struct ListTraversal;
347		/// impl<'a, A: 'a + Clone> TraversalFunc<'a, Vec<A>, Vec<A>, A, A> for ListTraversal {
348		/// 	fn apply<M: Applicative>(
349		/// 		&self,
350		/// 		f: Box<dyn Fn(A) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, A>) + 'a>,
351		/// 		s: Vec<A>,
352		/// 	) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, Vec<A>>) {
353		/// 		s.into_iter().fold(M::pure(vec![]), |acc, a| {
354		/// 			M::lift2(
355		/// 				|mut v: Vec<A>, x: A| {
356		/// 					v.push(x);
357		/// 					v
358		/// 				},
359		/// 				acc,
360		/// 				f(a),
361		/// 			)
362		/// 		})
363		/// 	}
364		/// }
365		///
366		/// let traversal = Traversal::<'_, RcBrand, Vec<i32>, Vec<i32>, i32, i32, _>::new(ListTraversal);
367		/// let f = std::rc::Rc::new(|x: i32| x + 1) as std::rc::Rc<dyn Fn(i32) -> i32>;
368		/// let result: std::rc::Rc<dyn Fn(Vec<i32>) -> Vec<i32>> =
369		/// 	TraversalOptic::evaluate::<RcFnBrand>(&traversal, f);
370		/// assert_eq!(result(vec![1, 2]), vec![2, 3]);
371		/// ```
372		fn evaluate<Q: Wander>(
373			&self,
374			pab: Apply!(<Q as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, A, B>),
375		) -> Apply!(<Q as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, S, T>) {
376			Optic::<Q, S, T, A, B>::evaluate(self, pab)
377		}
378	}
379
380	#[document_type_parameters(
381		"The lifetime of the values.",
382		"The reference-counted pointer type.",
383		"The source type of the structure.",
384		"The source type of the focus.",
385		"The type of the traversal function."
386	)]
387	#[document_parameters("The traversal instance.")]
388	impl<'a, PointerBrand, S, A, F> FoldOptic<'a, S, A> for Traversal<'a, PointerBrand, S, S, A, A, F>
389	where
390		PointerBrand: ToDynCloneFn,
391		F: TraversalFunc<'a, S, S, A, A> + Clone + 'a,
392		S: 'a,
393		A: 'a + Clone,
394	{
395		#[document_signature]
396		#[document_type_parameters(
397			"The monoid type.",
398			"The reference-counted pointer type for the Forget brand."
399		)]
400		#[document_parameters("The profunctor value to transform.")]
401		#[document_returns("The transformed profunctor value.")]
402		#[document_examples]
403		///
404		/// ```
405		/// use fp_library::{
406		/// 	Apply,
407		/// 	brands::{
408		/// 		optics::*,
409		/// 		*,
410		/// 	},
411		/// 	classes::{
412		/// 		Applicative,
413		/// 		lift::Lift,
414		/// 		optics::{
415		/// 			traversal::TraversalFunc,
416		/// 			*,
417		/// 		},
418		/// 		pointed::Pointed,
419		/// 		profunctor::*,
420		/// 	},
421		/// 	kinds::*,
422		/// 	types::optics::{
423		/// 		Forget,
424		/// 		Traversal,
425		/// 	},
426		/// };
427		///
428		/// #[derive(Clone)]
429		/// struct ListTraversal;
430		/// impl<'a, A: 'a + Clone> TraversalFunc<'a, Vec<A>, Vec<A>, A, A> for ListTraversal {
431		/// 	fn apply<M: Applicative>(
432		/// 		&self,
433		/// 		f: Box<dyn Fn(A) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, A>) + 'a>,
434		/// 		s: Vec<A>,
435		/// 	) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, Vec<A>>) {
436		/// 		s.into_iter().fold(M::pure(vec![]), |acc, a| {
437		/// 			M::lift2(
438		/// 				|mut v: Vec<A>, x: A| {
439		/// 					v.push(x);
440		/// 					v
441		/// 				},
442		/// 				acc,
443		/// 				f(a),
444		/// 			)
445		/// 		})
446		/// 	}
447		/// }
448		///
449		/// let traversal = Traversal::<'_, RcBrand, Vec<i32>, Vec<i32>, i32, i32, _>::new(ListTraversal);
450		/// let f = Forget::<RcBrand, String, i32, i32>::new(|x: i32| x.to_string());
451		/// let result = FoldOptic::evaluate(&traversal, f);
452		/// assert_eq!(result.run(vec![1, 2]), "12".to_string());
453		/// ```
454		fn evaluate<R: 'a + Monoid + Clone + 'static, Q: ToDynCloneFn + 'static>(
455			&self,
456			pab: Apply!(<ForgetBrand<Q, R> as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, A, A>),
457		) -> Apply!(<ForgetBrand<Q, R> as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, S, S>)
458		{
459			TraversalOptic::evaluate::<ForgetBrand<Q, R>>(self, pab)
460		}
461	}
462
463	#[document_type_parameters(
464		"The lifetime of the values.",
465		"The reference-counted pointer type for the setter brand.",
466		"The reference-counted pointer type for the traversal.",
467		"The source type of the structure.",
468		"The target type of the structure after an update.",
469		"The source type of the focus.",
470		"The target type of the focus after an update.",
471		"The type of the traversal function."
472	)]
473	#[document_parameters("The traversal instance.")]
474	impl<'a, Q, PointerBrand, S, T, A, B, F> SetterOptic<'a, Q, S, T, A, B>
475		for Traversal<'a, PointerBrand, S, T, A, B, F>
476	where
477		PointerBrand: ToDynCloneFn,
478		Q: ToDynCloneFn,
479		F: TraversalFunc<'a, S, T, A, B> + Clone + 'a,
480		S: 'a,
481		T: 'a,
482		A: 'a,
483		B: 'a + Clone,
484	{
485		#[document_signature]
486		#[document_parameters("The profunctor value to transform.")]
487		#[document_returns("The transformed profunctor value.")]
488		#[document_examples]
489		///
490		/// ```
491		/// use fp_library::{
492		/// 	Apply,
493		/// 	brands::*,
494		/// 	classes::{
495		/// 		Applicative,
496		/// 		lift::Lift,
497		/// 		optics::{
498		/// 			traversal::TraversalFunc,
499		/// 			*,
500		/// 		},
501		/// 		pointed::Pointed,
502		/// 		profunctor::*,
503		/// 	},
504		/// 	kinds::*,
505		/// 	types::optics::Traversal,
506		/// };
507		///
508		/// #[derive(Clone)]
509		/// struct ListTraversal;
510		/// impl<'a, A: 'a + Clone> TraversalFunc<'a, Vec<A>, Vec<A>, A, A> for ListTraversal {
511		/// 	fn apply<M: Applicative>(
512		/// 		&self,
513		/// 		f: Box<dyn Fn(A) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, A>) + 'a>,
514		/// 		s: Vec<A>,
515		/// 	) -> Apply!(<M as Kind!( type Of<'b, U: 'b>: 'b; )>::Of<'a, Vec<A>>) {
516		/// 		s.into_iter().fold(M::pure(vec![]), |acc, a| {
517		/// 			M::lift2(
518		/// 				|mut v: Vec<A>, x: A| {
519		/// 					v.push(x);
520		/// 					v
521		/// 				},
522		/// 				acc,
523		/// 				f(a),
524		/// 			)
525		/// 		})
526		/// 	}
527		/// }
528		///
529		/// let traversal = Traversal::<'_, RcBrand, Vec<i32>, Vec<i32>, i32, i32, _>::new(ListTraversal);
530		/// let f = std::rc::Rc::new(|x: i32| x + 1) as std::rc::Rc<dyn Fn(i32) -> i32>;
531		/// let result: std::rc::Rc<dyn Fn(Vec<i32>) -> Vec<i32>> =
532		/// 	SetterOptic::<RcBrand, _, _, _, _>::evaluate(&traversal, f);
533		/// assert_eq!(result(vec![1, 2]), vec![2, 3]);
534		/// ```
535		fn evaluate(
536			&self,
537			pab: Apply!(<FnBrand<Q> as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, A, B>),
538		) -> Apply!(<FnBrand<Q> as Kind!( type Of<'b, T: 'b, U: 'b>: 'b; )>::Of<'a, S, T>) {
539			TraversalOptic::evaluate::<FnBrand<Q>>(self, pab)
540		}
541	}
542}
543pub use inner::*;