Skip to main content

fp_library/classes/
send_ref_foldable_with_index.rs

1//! Thread-safe by-reference variant of [`FoldableWithIndex`](crate::classes::FoldableWithIndex).
2//!
3//! **User story:** "I want to fold over a thread-safe memoized value by reference, with access to the index."
4//!
5//! All three methods (`send_ref_fold_map_with_index`, `send_ref_fold_right_with_index`,
6//! `send_ref_fold_left_with_index`) have default implementations in terms of each other,
7//! so implementors only need to provide one.
8//!
9//! ### Examples
10//!
11//! ```
12//! use fp_library::{
13//! 	brands::*,
14//! 	classes::send_ref_foldable_with_index::SendRefFoldableWithIndex,
15//! 	types::*,
16//! };
17//!
18//! let lazy = ArcLazy::new(|| 42);
19//! let result =
20//! 	<LazyBrand<ArcLazyConfig> as SendRefFoldableWithIndex>::send_ref_fold_map_with_index::<
21//! 		ArcFnBrand,
22//! 		_,
23//! 		_,
24//! 	>(|_, x: &i32| x.to_string(), &lazy);
25//! assert_eq!(result, "42");
26//! ```
27
28#[fp_macros::document_module]
29mod inner {
30	use {
31		crate::{
32			classes::{
33				send_clone_fn::SendLiftFn,
34				*,
35			},
36			kinds::*,
37			types::{
38				Dual,
39				SendEndofunction,
40			},
41		},
42		fp_macros::*,
43	};
44
45	/// Thread-safe by-reference folding with index over a structure.
46	///
47	/// Similar to [`RefFoldableWithIndex`], but closures and elements must be `Send + Sync`.
48	///
49	/// All three methods (`send_ref_fold_map_with_index`, `send_ref_fold_right_with_index`,
50	/// `send_ref_fold_left_with_index`) have default implementations in terms of each other,
51	/// so implementors only need to provide one.
52	#[kind(type Of<'a, A: 'a>: 'a;)]
53	pub trait SendRefFoldableWithIndex: SendRefFoldable + WithIndex {
54		/// Maps each element to a monoid by reference with index (thread-safe).
55		#[document_signature]
56		#[document_type_parameters(
57			"The lifetime of the values.",
58			"The brand of the cloneable function to use.",
59			"The type of the elements.",
60			"The monoid type."
61		)]
62		#[document_parameters(
63			"The function to apply to each element's index and reference.",
64			"The structure to fold over."
65		)]
66		#[document_returns("The combined result.")]
67		#[document_examples]
68		///
69		/// ```
70		/// use fp_library::{
71		/// 	brands::*,
72		/// 	classes::send_ref_foldable_with_index::SendRefFoldableWithIndex,
73		/// 	types::*,
74		/// };
75		///
76		/// let lazy = ArcLazy::new(|| 42);
77		/// let result =
78		/// 	<LazyBrand<ArcLazyConfig> as SendRefFoldableWithIndex>::send_ref_fold_map_with_index::<
79		/// 		ArcFnBrand,
80		/// 		_,
81		/// 		_,
82		/// 	>(|_, x: &i32| x.to_string(), &lazy);
83		/// assert_eq!(result, "42");
84		/// ```
85		fn send_ref_fold_map_with_index<
86			'a,
87			FnBrand,
88			A: Send + Sync + 'a + Clone,
89			R: Monoid + Send + Sync + 'a,
90		>(
91			f: impl Fn(Self::Index, &A) -> R + Send + Sync + 'a,
92			fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
93		) -> R
94		where
95			FnBrand: SendLiftFn + 'a,
96			Self::Index: Send + Sync + 'a, {
97			Self::send_ref_fold_right_with_index::<FnBrand, A, R>(
98				move |i, a: &A, acc| Semigroup::append(f(i, a), acc),
99				Monoid::empty(),
100				fa,
101			)
102		}
103
104		/// Folds the structure from the right by reference with index (thread-safe).
105		#[document_signature]
106		#[document_type_parameters(
107			"The lifetime of the values.",
108			"The brand of the cloneable function to use.",
109			"The type of the elements.",
110			"The type of the accumulator."
111		)]
112		#[document_parameters(
113			"The function to apply to each element's index, reference, and accumulator.",
114			"The initial value of the accumulator.",
115			"The structure to fold over."
116		)]
117		#[document_returns("The final accumulator value.")]
118		#[document_examples]
119		///
120		/// ```
121		/// use fp_library::{
122		/// 	brands::*,
123		/// 	classes::send_ref_foldable_with_index::SendRefFoldableWithIndex,
124		/// 	types::*,
125		/// };
126		///
127		/// let lazy = ArcLazy::new(|| 10);
128		/// let result =
129		/// 	<LazyBrand<ArcLazyConfig> as SendRefFoldableWithIndex>::send_ref_fold_right_with_index::<
130		/// 		ArcFnBrand,
131		/// 		_,
132		/// 		_,
133		/// 	>(|_, x: &i32, acc: i32| acc + *x, 0, &lazy);
134		/// assert_eq!(result, 10);
135		/// ```
136		fn send_ref_fold_right_with_index<
137			'a,
138			FnBrand,
139			A: Send + Sync + 'a + Clone,
140			B: Send + Sync + 'a,
141		>(
142			func: impl Fn(Self::Index, &A, B) -> B + Send + Sync + 'a,
143			initial: B,
144			fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
145		) -> B
146		where
147			FnBrand: SendLiftFn + 'a,
148			Self::Index: Send + Sync + 'a, {
149			let f =
150				<FnBrand as SendLiftFn>::new(move |(i, a, b): (Self::Index, A, B)| func(i, &a, b));
151			let m = Self::send_ref_fold_map_with_index::<FnBrand, A, SendEndofunction<FnBrand, B>>(
152				move |i, a: &A| {
153					let a = a.clone();
154					let f = f.clone();
155					SendEndofunction::<FnBrand, B>::new(<FnBrand as SendLiftFn>::new(move |b| {
156						let a = a.clone();
157						let i = i.clone();
158						f((i, a, b))
159					}))
160				},
161				fa,
162			);
163			m.0(initial)
164		}
165
166		/// Folds the structure from the left by reference with index (thread-safe).
167		#[document_signature]
168		#[document_type_parameters(
169			"The lifetime of the values.",
170			"The brand of the cloneable function to use.",
171			"The type of the elements.",
172			"The type of the accumulator."
173		)]
174		#[document_parameters(
175			"The function to apply to the accumulator, each element's index, and reference.",
176			"The initial value of the accumulator.",
177			"The structure to fold over."
178		)]
179		#[document_returns("The final accumulator value.")]
180		#[document_examples]
181		///
182		/// ```
183		/// use fp_library::{
184		/// 	brands::*,
185		/// 	classes::send_ref_foldable_with_index::SendRefFoldableWithIndex,
186		/// 	types::*,
187		/// };
188		///
189		/// let lazy = ArcLazy::new(|| 10);
190		/// let result =
191		/// 	<LazyBrand<ArcLazyConfig> as SendRefFoldableWithIndex>::send_ref_fold_left_with_index::<
192		/// 		ArcFnBrand,
193		/// 		_,
194		/// 		_,
195		/// 	>(|_, acc: i32, x: &i32| acc + *x, 0, &lazy);
196		/// assert_eq!(result, 10);
197		/// ```
198		fn send_ref_fold_left_with_index<
199			'a,
200			FnBrand,
201			A: Send + Sync + 'a + Clone,
202			B: Send + Sync + 'a,
203		>(
204			func: impl Fn(Self::Index, B, &A) -> B + Send + Sync + 'a,
205			initial: B,
206			fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
207		) -> B
208		where
209			FnBrand: SendLiftFn + 'a,
210			Self::Index: Send + Sync + 'a, {
211			let f =
212				<FnBrand as SendLiftFn>::new(move |(i, b, a): (Self::Index, B, A)| func(i, b, &a));
213			let m = Self::send_ref_fold_map_with_index::<
214				FnBrand,
215				A,
216				Dual<SendEndofunction<FnBrand, B>>,
217			>(
218				move |i, a: &A| {
219					let a = a.clone();
220					let f = f.clone();
221					Dual(SendEndofunction::<FnBrand, B>::new(<FnBrand as SendLiftFn>::new(
222						move |b| {
223							let a = a.clone();
224							let i = i.clone();
225							f((i, b, a))
226						},
227					)))
228				},
229				fa,
230			);
231			(m.0).0(initial)
232		}
233	}
234
235	/// Maps each element to a monoid by reference with its index (thread-safe).
236	///
237	/// Free function version that dispatches to [the type class' associated function][`SendRefFoldableWithIndex::send_ref_fold_map_with_index`].
238	#[document_signature]
239	#[document_type_parameters(
240		"The lifetime of the values.",
241		"The brand of the cloneable function to use.",
242		"The brand of the structure.",
243		"The type of the elements.",
244		"The monoid type."
245	)]
246	#[document_parameters(
247		"The function to apply to each element's index and reference.",
248		"The structure to fold over."
249	)]
250	#[document_returns("The combined result.")]
251	#[document_examples]
252	///
253	/// ```
254	/// use fp_library::{
255	/// 	brands::*,
256	/// 	functions::*,
257	/// 	types::*,
258	/// };
259	///
260	/// let lazy = ArcLazy::new(|| 42);
261	/// let result = send_ref_fold_map_with_index::<ArcFnBrand, LazyBrand<ArcLazyConfig>, _, _>(
262	/// 	|_, x: &i32| x.to_string(),
263	/// 	&lazy,
264	/// );
265	/// assert_eq!(result, "42");
266	/// ```
267	pub fn send_ref_fold_map_with_index<
268		'a,
269		FnBrand: SendLiftFn + 'a,
270		Brand: SendRefFoldableWithIndex,
271		A: Send + Sync + 'a + Clone,
272		R: Monoid + Send + Sync + 'a,
273	>(
274		f: impl Fn(Brand::Index, &A) -> R + Send + Sync + 'a,
275		fa: &Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
276	) -> R
277	where
278		Brand::Index: Send + Sync + 'a, {
279		Brand::send_ref_fold_map_with_index::<FnBrand, A, R>(f, fa)
280	}
281}
282
283pub use inner::*;