Skip to main content

fp_library/dispatch/
semiapplicative.rs

1//! Dispatch for [`Semiapplicative::apply`](crate::classes::Semiapplicative::apply)
2//! and [`RefSemiapplicative::ref_apply`](crate::classes::RefSemiapplicative::ref_apply).
3//!
4//! Provides unified Val/Ref dispatch via the [`ApplyDispatch`] trait, the
5//! [`InferableFnBrand`] trait for FnBrand inference, and an inference wrapper
6//! [`apply`] that infers both Brand and FnBrand from the container types.
7//!
8//! ### Examples
9//!
10//! ```
11//! use fp_library::{
12//! 	brands::*,
13//! 	classes::*,
14//! 	functions::*,
15//! };
16//!
17//! // Val: owned containers, Fn(A) -> B closures
18//! let f = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
19//! let x = Some(5);
20//! let y: Option<i32> = apply(f, x);
21//! assert_eq!(y, Some(10));
22//!
23//! // Ref: borrowed containers, Fn(&A) -> B closures
24//! let f = Some(std::rc::Rc::new(|x: &i32| *x * 2) as std::rc::Rc<dyn Fn(&i32) -> i32>);
25//! let x = Some(5);
26//! let y: Option<i32> = apply(&f, &x);
27//! assert_eq!(y, Some(10));
28//! ```
29
30#[fp_macros::document_module]
31pub(crate) mod inner {
32	use {
33		crate::{
34			classes::{
35				CloneFn,
36				RefSemiapplicative,
37				Semiapplicative,
38			},
39			dispatch::{
40				ClosureMode,
41				Ref,
42				Val,
43			},
44			kinds::*,
45		},
46		fp_macros::*,
47	};
48
49	/// Maps a concrete wrapped-function type back to its `FnBrand`,
50	/// parameterized by closure mode.
51	///
52	/// Each concrete wrapper type has impls for both Val and Ref modes:
53	/// - Val: `Rc<dyn Fn(A) -> B>` -> `RcFnBrand`
54	/// - Ref: `Rc<dyn Fn(&A) -> B>` -> `RcFnBrand`
55	///
56	/// The Mode parameter disambiguates the two, since
57	/// `Rc<dyn Fn(&A) -> B>` would otherwise match the Val impl with
58	/// `A` = `&A'`.
59	#[document_type_parameters(
60		"The function-wrapping brand (e.g., RcFnBrand, ArcFnBrand).",
61		"The input type of the wrapped function.",
62		"The output type of the wrapped function.",
63		"The closure mode (Val or Ref)."
64	)]
65	pub trait InferableFnBrand<FnBrand, A, B, Mode = Val> {}
66
67	// -- RcFnBrand impls --
68
69	/// Maps `Rc<dyn Fn(A) -> B>` back to `RcFnBrand` (Val mode).
70	#[document_type_parameters(
71		"The lifetime of the wrapped function.",
72		"The input type of the wrapped function.",
73		"The output type of the wrapped function."
74	)]
75	impl<'a, A: 'a, B: 'a> InferableFnBrand<crate::brands::RcFnBrand, A, B, Val>
76		for std::rc::Rc<dyn 'a + Fn(A) -> B>
77	{
78	}
79
80	/// Maps `Rc<dyn Fn(&A) -> B>` back to `RcFnBrand` (Ref mode).
81	#[document_type_parameters(
82		"The lifetime of the wrapped function.",
83		"The input type of the wrapped function.",
84		"The output type of the wrapped function."
85	)]
86	impl<'a, A: 'a, B: 'a> InferableFnBrand<crate::brands::RcFnBrand, A, B, Ref>
87		for std::rc::Rc<dyn 'a + Fn(&A) -> B>
88	{
89	}
90
91	// -- ArcFnBrand impls --
92
93	/// Maps `Arc<dyn Fn(A) -> B + Send + Sync>` back to `ArcFnBrand` (Val mode).
94	#[document_type_parameters(
95		"The lifetime of the wrapped function.",
96		"The input type of the wrapped function.",
97		"The output type of the wrapped function."
98	)]
99	impl<'a, A: 'a, B: 'a> InferableFnBrand<crate::brands::ArcFnBrand, A, B, Val>
100		for std::sync::Arc<dyn 'a + Fn(A) -> B + Send + Sync>
101	{
102	}
103
104	/// Maps `Arc<dyn Fn(&A) -> B + Send + Sync>` back to `ArcFnBrand` (Ref mode).
105	#[document_type_parameters(
106		"The lifetime of the wrapped function.",
107		"The input type of the wrapped function.",
108		"The output type of the wrapped function."
109	)]
110	impl<'a, A: 'a, B: 'a> InferableFnBrand<crate::brands::ArcFnBrand, A, B, Ref>
111		for std::sync::Arc<dyn 'a + Fn(&A) -> B + Send + Sync>
112	{
113	}
114
115	// -- ApplyDispatch trait --
116
117	/// Trait that routes an apply operation to the appropriate type class method.
118	///
119	/// The `Marker` type parameter selects Val or Ref dispatch:
120	/// - Val: routes to [`Semiapplicative::apply`] (owned containers, `Fn(A) -> B`)
121	/// - Ref: routes to [`RefSemiapplicative::ref_apply`] (borrowed containers, `Fn(&A) -> B`)
122	#[document_type_parameters(
123		"The lifetime of the values.",
124		"The function-wrapping brand.",
125		"The brand of the applicative.",
126		"The type of the value(s) inside the value container.",
127		"The result type after applying the function.",
128		"The concrete wrapped-function type.",
129		"The value container type.",
130		"Dispatch marker type, inferred automatically."
131	)]
132	#[document_parameters("The function container implementing this dispatch.")]
133	pub trait ApplyDispatch<
134		'a,
135		FnBrand,
136		Brand: Kind_cdc7cd43dac7585f,
137		A: 'a,
138		B: 'a,
139		WrappedFn: 'a,
140		FA,
141		Marker,
142	> {
143		/// Perform the dispatched apply operation.
144		#[document_signature]
145		///
146		#[document_parameters("The value container to apply the function(s) to.")]
147		///
148		#[document_returns("A new container with the function(s) applied to the value(s).")]
149		#[document_examples]
150		///
151		/// ```
152		/// use fp_library::{
153		/// 	brands::*,
154		/// 	classes::*,
155		/// 	functions::*,
156		/// };
157		///
158		/// let f = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
159		/// let y: Option<i32> = apply(f, Some(5));
160		/// assert_eq!(y, Some(10));
161		/// ```
162		fn dispatch(
163			self,
164			fa: FA,
165		) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>);
166	}
167
168	// -- Val impl: owned containers -> Semiapplicative::apply --
169
170	/// Routes owned containers to [`Semiapplicative::apply`].
171	#[document_type_parameters(
172		"The lifetime of the values.",
173		"The function-wrapping brand.",
174		"The brand of the applicative.",
175		"The type of the value(s) inside the value container.",
176		"The result type after applying the function.",
177		"The concrete wrapped-function type."
178	)]
179	#[document_parameters("The owned function container.")]
180	impl<'a, FnBrand, Brand, A, B, WrappedFn>
181		ApplyDispatch<
182			'a,
183			FnBrand,
184			Brand,
185			A,
186			B,
187			WrappedFn,
188			Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
189			Val,
190		> for Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, WrappedFn>)
191	where
192		FnBrand: CloneFn + 'a,
193		Brand: Semiapplicative,
194		A: Clone + 'a,
195		B: 'a,
196		WrappedFn: Clone + 'a,
197		Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, WrappedFn>): Into<
198			Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, A, B>>),
199		>,
200	{
201		#[document_signature]
202		///
203		#[document_parameters("The value container to apply the function(s) to.")]
204		///
205		#[document_returns("A new container with the function(s) applied to the value(s).")]
206		#[document_examples]
207		///
208		/// ```
209		/// use fp_library::{
210		/// 	brands::*,
211		/// 	classes::*,
212		/// 	functions::*,
213		/// };
214		///
215		/// let f = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
216		/// let y: Option<i32> = apply(f, Some(5));
217		/// assert_eq!(y, Some(10));
218		/// ```
219		fn dispatch(
220			self,
221			fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
222		) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
223			Brand::apply::<FnBrand, A, B>(self.into(), fa)
224		}
225	}
226
227	// -- Ref impl: borrowed containers -> RefSemiapplicative::ref_apply --
228
229	/// Routes borrowed containers to [`RefSemiapplicative::ref_apply`].
230	#[document_type_parameters(
231		"The lifetime of the values.",
232		"The borrow lifetime.",
233		"The function-wrapping brand.",
234		"The brand of the applicative.",
235		"The type of the value(s) inside the value container.",
236		"The result type after applying the function.",
237		"The concrete wrapped-function type."
238	)]
239	#[document_parameters("The borrowed function container.")]
240	impl<'a, 'b, FnBrand, Brand, A, B, WrappedFn>
241		ApplyDispatch<
242			'a,
243			FnBrand,
244			Brand,
245			A,
246			B,
247			WrappedFn,
248			&'b Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
249			Ref,
250		> for &'b Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, WrappedFn>)
251	where
252		'a: 'b,
253		FnBrand: CloneFn<Ref> + 'a,
254		Brand: RefSemiapplicative,
255		A: 'a,
256		B: 'a,
257		WrappedFn: 'a,
258		&'b Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, WrappedFn>): Into<
259			&'b Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn<Ref>>::Of<'a, A, B>>),
260		>,
261	{
262		#[document_signature]
263		///
264		#[document_parameters("The borrowed value container to apply the function(s) to.")]
265		///
266		#[document_returns("A new container with the function(s) applied to the value(s).")]
267		#[document_examples]
268		///
269		/// ```
270		/// use fp_library::{
271		/// 	brands::*,
272		/// 	functions::*,
273		/// };
274		///
275		/// let f = Some(std::rc::Rc::new(|x: &i32| *x * 2) as std::rc::Rc<dyn Fn(&i32) -> i32>);
276		/// let x = Some(5);
277		/// let y: Option<i32> = apply(&f, &x);
278		/// assert_eq!(y, Some(10));
279		/// ```
280		fn dispatch(
281			self,
282			fa: &'b Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
283		) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
284			Brand::ref_apply::<FnBrand, A, B>(self.into(), fa)
285		}
286	}
287
288	// -- Inference wrapper --
289
290	/// Applies a container of functions to a container of values, inferring
291	/// Brand, FnBrand, and Val/Ref dispatch from the container types.
292	///
293	/// - Brand is resolved via dual InferableBrand bounds on FF and FA.
294	/// - FnBrand is resolved via [`InferableFnBrand`] from the wrapper type WrappedFn.
295	/// - Val/Ref dispatch is resolved via the Marker projected from FA's InferableBrand.
296	/// - The `CloneFn` mode is tied to the Marker via `CloneFn<Marker>`.
297	///
298	/// No turbofish arguments are needed for single-brand types.
299	///
300	/// For types where inference fails (e.g., multi-brand types like Result),
301	/// use [`explicit::apply`](crate::functions::explicit::apply) with a turbofish.
302	#[document_signature]
303	///
304	#[document_type_parameters(
305		"The lifetime of the values.",
306		"The function-wrapping brand, inferred via InferableFnBrand.",
307		"The brand, inferred via InferableBrand from FF and FA.",
308		"The type of the value(s) inside the value container.",
309		"The result type after applying the function.",
310		"The concrete wrapped-function type, inferred from FF's element type.",
311		"The function container type.",
312		"The value container type."
313	)]
314	///
315	#[document_parameters(
316		"The container of function(s) to apply.",
317		"The container of value(s) to apply the function(s) to."
318	)]
319	///
320	#[document_returns("A new container with the function(s) applied to the value(s).")]
321	#[document_examples]
322	///
323	/// ```
324	/// use fp_library::{
325	/// 	brands::*,
326	/// 	classes::*,
327	/// 	functions::*,
328	/// };
329	///
330	/// // Val: owned containers
331	/// let f = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
332	/// let y: Option<i32> = apply(f, Some(5));
333	/// assert_eq!(y, Some(10));
334	///
335	/// // Ref: borrowed containers
336	/// let f = Some(std::rc::Rc::new(|x: &i32| *x * 2) as std::rc::Rc<dyn Fn(&i32) -> i32>);
337	/// let x = Some(5);
338	/// let y: Option<i32> = apply(&f, &x);
339	/// assert_eq!(y, Some(10));
340	/// ```
341	#[allow_named_generics]
342	pub fn apply<'a, FnBrand, Brand, A, B, WrappedFn, FF, FA>(
343		ff: FF,
344		fa: FA,
345	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
346	where
347		Brand: Kind_cdc7cd43dac7585f,
348		A: 'a,
349		B: 'a,
350		WrappedFn: 'a,
351		FA: InferableBrand_cdc7cd43dac7585f<'a, Brand, A>,
352		<FA as InferableBrand_cdc7cd43dac7585f<'a, Brand, A>>::Marker: ClosureMode,
353		FnBrand: CloneFn<<FA as InferableBrand_cdc7cd43dac7585f<'a, Brand, A>>::Marker> + 'a,
354		WrappedFn: InferableFnBrand<
355				FnBrand,
356				A,
357				B,
358				<FA as InferableBrand_cdc7cd43dac7585f<'a, Brand, A>>::Marker,
359			>,
360		FF: InferableBrand_cdc7cd43dac7585f<'a, Brand, WrappedFn>
361			+ ApplyDispatch<
362				'a,
363				FnBrand,
364				Brand,
365				A,
366				B,
367				WrappedFn,
368				FA,
369				<FA as InferableBrand_cdc7cd43dac7585f<'a, Brand, A>>::Marker,
370			>, {
371		ff.dispatch(fa)
372	}
373}
374
375pub use inner::*;