fp_library/dispatch/functor.rs
1//! Dispatch for [`Functor::map`](crate::classes::Functor::map) and
2//! [`RefFunctor::ref_map`](crate::classes::RefFunctor::ref_map).
3//!
4//! Provides the [`FunctorDispatch`] trait and a unified [`explicit::map`] free
5//! function that routes to the appropriate trait method based on the closure's
6//! argument type.
7//!
8//! ### Examples
9//!
10//! ```
11//! use fp_library::{
12//! brands::*,
13//! functions::explicit::*,
14//! types::*,
15//! };
16//!
17//! // Owned: dispatches to Functor::map
18//! let y = map::<OptionBrand, _, _, _, _>(|x: i32| x * 2, Some(5));
19//! assert_eq!(y, Some(10));
20//!
21//! // By-ref: dispatches to RefFunctor::ref_map
22//! let lazy = RcLazy::pure(10);
23//! let mapped = map::<LazyBrand<RcLazyConfig>, _, _, _, _>(|x: &i32| *x * 2, &lazy);
24//! assert_eq!(*mapped.evaluate(), 20);
25//! ```
26
27#[fp_macros::document_module]
28pub(crate) mod inner {
29 use {
30 crate::{
31 classes::{
32 Functor,
33 RefFunctor,
34 },
35 dispatch::{
36 Ref,
37 Val,
38 },
39 kinds::*,
40 },
41 fp_macros::*,
42 };
43
44 /// Trait that routes a map operation to the appropriate type class method.
45 ///
46 /// The `Marker` type parameter is an implementation detail resolved by
47 /// the compiler from the closure's argument type; callers never specify
48 /// it directly. The `FA` type parameter is inferred from the container
49 /// argument: owned for Val dispatch, borrowed for Ref dispatch.
50 #[document_type_parameters(
51 "The lifetime of the values.",
52 "The brand of the functor.",
53 "The type of the value(s) inside the functor.",
54 "The type of the result(s) of applying the function.",
55 "The container type (owned or borrowed), inferred from the argument.",
56 "Dispatch marker type, inferred automatically. Either [`Val`](crate::dispatch::Val) or [`Ref`](crate::dispatch::Ref)."
57 )]
58 #[document_parameters("The closure implementing this dispatch.")]
59 pub trait FunctorDispatch<'a, Brand: Kind_cdc7cd43dac7585f, A: 'a, B: 'a, FA, Marker> {
60 /// Perform the dispatched map operation.
61 #[document_signature]
62 ///
63 #[document_parameters("The functor instance containing the value(s).")]
64 ///
65 #[document_returns(
66 "A new functor instance containing the result(s) of applying the function."
67 )]
68 #[document_examples]
69 ///
70 /// ```
71 /// use fp_library::{
72 /// brands::*,
73 /// functions::explicit::*,
74 /// types::*,
75 /// };
76 ///
77 /// let result = map::<OptionBrand, _, _, _, _>(|x: i32| x * 2, Some(5));
78 /// assert_eq!(result, Some(10));
79 /// ```
80 fn dispatch(
81 self,
82 fa: FA,
83 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>);
84 }
85
86 // -- Val: Fn(A) -> B -> Functor::map --
87
88 /// Routes `Fn(A) -> B` closures to [`Functor::map`].
89 #[document_type_parameters(
90 "The lifetime of the values.",
91 "The brand of the functor.",
92 "The type of the value(s) inside the functor.",
93 "The type of the result(s) of applying the function.",
94 "The closure type."
95 )]
96 #[document_parameters("The closure that takes owned values.")]
97 impl<'a, Brand, A, B, F>
98 FunctorDispatch<
99 'a,
100 Brand,
101 A,
102 B,
103 Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
104 Val,
105 > for F
106 where
107 Brand: Functor,
108 A: 'a,
109 B: 'a,
110 F: Fn(A) -> B + 'a,
111 {
112 #[document_signature]
113 ///
114 #[document_parameters("The functor instance containing the value(s).")]
115 ///
116 #[document_returns(
117 "A new functor instance containing the result(s) of applying the function."
118 )]
119 #[document_examples]
120 ///
121 /// ```
122 /// use fp_library::{
123 /// brands::*,
124 /// functions::explicit::*,
125 /// };
126 ///
127 /// let result = map::<OptionBrand, _, _, _, _>(|x: i32| x * 2, Some(5));
128 /// assert_eq!(result, Some(10));
129 /// ```
130 fn dispatch(
131 self,
132 fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
133 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
134 Brand::map(self, fa)
135 }
136 }
137
138 // -- Ref: Fn(&A) -> B -> RefFunctor::ref_map --
139
140 /// Routes `Fn(&A) -> B` closures to [`RefFunctor::ref_map`].
141 ///
142 /// The container must be passed by reference (`&fa`).
143 #[document_type_parameters(
144 "The lifetime of the values.",
145 "The borrow lifetime.",
146 "The brand of the functor.",
147 "The type of the value(s) inside the functor.",
148 "The type of the result(s) of applying the function.",
149 "The closure type."
150 )]
151 #[document_parameters("The closure that takes references.")]
152 impl<'a, 'b, Brand, A, B, F>
153 FunctorDispatch<
154 'a,
155 Brand,
156 A,
157 B,
158 &'b Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
159 Ref,
160 > for F
161 where
162 Brand: RefFunctor,
163 A: 'a,
164 B: 'a,
165 F: Fn(&A) -> B + 'a,
166 {
167 #[document_signature]
168 ///
169 #[document_parameters("A reference to the functor instance.")]
170 ///
171 #[document_returns(
172 "A new functor instance containing the result(s) of applying the function."
173 )]
174 #[document_examples]
175 ///
176 /// ```
177 /// use fp_library::{
178 /// brands::*,
179 /// functions::explicit::*,
180 /// types::*,
181 /// };
182 ///
183 /// let lazy = RcLazy::pure(10);
184 /// let result = map::<LazyBrand<RcLazyConfig>, _, _, _, _>(|x: &i32| *x * 2, &lazy);
185 /// assert_eq!(*result.evaluate(), 20);
186 /// ```
187 fn dispatch(
188 self,
189 fa: &'b Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
190 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
191 Brand::ref_map(self, fa)
192 }
193 }
194
195 // -- Inference wrapper --
196
197 /// Maps a function over a functor, inferring the brand from the container type.
198 ///
199 /// This is the primary API for mapping. The `Brand` type parameter is
200 /// inferred from the concrete type of `fa` via [`InferableBrand`](crate::kinds::InferableBrand_cdc7cd43dac7585f). Both
201 /// owned and borrowed containers are supported:
202 ///
203 /// - Owned: `map(|x: i32| x + 1, Some(5))` infers `OptionBrand`.
204 /// - Borrowed: `map(|x: &i32| *x + 1, &Some(5))` infers `OptionBrand`
205 /// via the blanket `impl InferableBrand for &T`.
206 ///
207 /// For types with multiple brands (e.g., `Result`), use
208 /// [`explicit::map`](crate::functions::explicit::map) with a turbofish.
209 #[document_signature]
210 ///
211 #[document_type_parameters(
212 "The lifetime of the values.",
213 "The container type (owned or borrowed). Brand is inferred from this.",
214 "The type of the value(s) inside the functor.",
215 "The type of the result(s) of applying the function.",
216 "Dispatch marker type, inferred automatically."
217 )]
218 ///
219 #[document_parameters(
220 "The function to apply to the value(s).",
221 "The functor instance (owned or borrowed)."
222 )]
223 ///
224 #[document_returns("A new functor instance containing the result(s) of applying the function.")]
225 #[document_examples]
226 ///
227 /// ```
228 /// use fp_library::functions::*;
229 ///
230 /// // Brand inferred from Option<i32>
231 /// assert_eq!(map(|x: i32| x * 2, Some(5)), Some(10));
232 ///
233 /// // Brand inferred from &Vec<i32> via blanket impl
234 /// let v = vec![1, 2, 3];
235 /// assert_eq!(map(|x: &i32| *x + 10, &v), vec![11, 12, 13]);
236 /// ```
237 pub fn map<'a, FA, A: 'a, B: 'a, Marker>(
238 f: impl FunctorDispatch<'a, <FA as InferableBrand_cdc7cd43dac7585f>::Brand, A, B, FA, Marker>,
239 fa: FA,
240 ) -> Apply!(<<FA as InferableBrand!(type Of<'a, A: 'a>: 'a;)>::Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
241 where
242 FA: InferableBrand_cdc7cd43dac7585f, {
243 f.dispatch(fa)
244 }
245
246 // -- Explicit dispatch free function --
247
248 /// Explicit dispatch functions requiring a Brand turbofish.
249 ///
250 /// For most use cases, prefer the inference-enabled wrappers from
251 /// [`functions`](crate::functions).
252 pub mod explicit {
253 use super::*;
254
255 /// Maps a function over the values in a functor context.
256 ///
257 /// Dispatches to either [`Functor::map`] or [`RefFunctor::ref_map`]
258 /// based on the closure's argument type:
259 ///
260 /// - If the closure takes owned values (`Fn(A) -> B`) and the container is
261 /// owned, dispatches to [`Functor::map`].
262 /// - If the closure takes references (`Fn(&A) -> B`) and the container is
263 /// borrowed (`&fa`), dispatches to [`RefFunctor::ref_map`].
264 ///
265 /// The `Marker` and `FA` type parameters are inferred automatically by the
266 /// compiler from the closure's argument type and the container argument.
267 /// Callers write `map::<Brand, _, _, _, _>(...)` and never need to specify
268 /// `Marker` or `FA` explicitly.
269 ///
270 /// The dispatch is resolved at compile time with no runtime cost.
271 #[document_signature]
272 ///
273 #[document_type_parameters(
274 "The lifetime of the values.",
275 "The brand of the functor.",
276 "The type of the value(s) inside the functor.",
277 "The type of the result(s) of applying the function.",
278 "The container type (owned or borrowed), inferred from the argument.",
279 "Dispatch marker type, inferred automatically."
280 )]
281 ///
282 #[document_parameters(
283 "The function to apply to the value(s) inside the functor.",
284 "The functor instance (owned for Val, borrowed for Ref)."
285 )]
286 ///
287 #[document_returns(
288 "A new functor instance containing the result(s) of applying the function."
289 )]
290 ///
291 #[document_examples]
292 ///
293 /// ```
294 /// use fp_library::{
295 /// brands::*,
296 /// functions::explicit::*,
297 /// types::*,
298 /// };
299 ///
300 /// // Owned: dispatches to Functor::map
301 /// let y = map::<OptionBrand, _, _, _, _>(|x: i32| x * 2, Some(5));
302 /// assert_eq!(y, Some(10));
303 ///
304 /// // By-ref: dispatches to RefFunctor::ref_map
305 /// let lazy = RcLazy::pure(10);
306 /// let mapped = map::<LazyBrand<RcLazyConfig>, _, _, _, _>(|x: &i32| *x * 2, &lazy);
307 /// assert_eq!(*mapped.evaluate(), 20);
308 /// ```
309 pub fn map<'a, Brand: Kind_cdc7cd43dac7585f, A: 'a, B: 'a, FA, Marker>(
310 f: impl FunctorDispatch<'a, Brand, A, B, FA, Marker>,
311 fa: FA,
312 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
313 f.dispatch(fa)
314 }
315 }
316}
317
318pub use inner::*;