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 the `InferableBrand` trait. 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 multi-brand types (e.g., `Result`), the closure's input type
208 /// disambiguates which brand applies:
209 ///
210 /// - `map(|x: i32| x + 1, Ok::<i32, String>(5))` infers
211 /// `ResultErrAppliedBrand<String>` (maps over Ok).
212 /// - `map(|e: String| e.len(), Err::<i32, String>("hi".into()))` infers
213 /// `ResultOkAppliedBrand<i32>` (maps over Err).
214 ///
215 /// For diagonal cases where the closure cannot disambiguate (e.g.,
216 /// `Result<T, T>`), use [`explicit::map`](crate::functions::explicit::map)
217 /// with a turbofish.
218 ///
219 /// **Note:** Pre-bound closures (`let f = |x| x + 1; map(f, Ok(5))`) may
220 /// lose deferred inference context for multi-brand types, because the
221 /// closure's parameter type is committed before `map` can use it for brand
222 /// resolution. Annotate the closure parameter type explicitly in these
223 /// cases: `let f = |x: i32| x + 1;`.
224 #[document_signature]
225 ///
226 #[document_type_parameters(
227 "The lifetime of the values.",
228 "The container type (owned or borrowed). Brand is inferred from this.",
229 "The type of the value(s) inside the functor.",
230 "The type of the result(s) of applying the function.",
231 "The brand, inferred via InferableBrand from FA and the closure's input type."
232 )]
233 ///
234 #[document_parameters(
235 "The function to apply to the value(s).",
236 "The functor instance (owned or borrowed)."
237 )]
238 ///
239 #[document_returns("A new functor instance containing the result(s) of applying the function.")]
240 #[document_examples]
241 ///
242 /// ```
243 /// use fp_library::functions::*;
244 ///
245 /// // Brand inferred from Option<i32>
246 /// assert_eq!(map(|x: i32| x * 2, Some(5)), Some(10));
247 ///
248 /// // Brand inferred from &Vec<i32> via blanket impl
249 /// let v = vec![1, 2, 3];
250 /// assert_eq!(map(|x: &i32| *x + 10, &v), vec![11, 12, 13]);
251 /// ```
252 pub fn map<'a, FA, A: 'a, B: 'a, Brand>(
253 f: impl FunctorDispatch<
254 'a,
255 Brand,
256 A,
257 B,
258 FA,
259 <FA as InferableBrand_cdc7cd43dac7585f<'a, Brand, A>>::Marker,
260 >,
261 fa: FA,
262 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)
263 where
264 Brand: Kind_cdc7cd43dac7585f,
265 FA: InferableBrand_cdc7cd43dac7585f<'a, Brand, A>, {
266 f.dispatch(fa)
267 }
268
269 // -- Explicit dispatch free function --
270
271 /// Explicit dispatch functions requiring a Brand turbofish.
272 ///
273 /// For most use cases, prefer the inference-enabled wrappers from
274 /// [`functions`](crate::functions).
275 pub mod explicit {
276 use super::*;
277
278 /// Maps a function over the values in a functor context.
279 ///
280 /// Dispatches to either [`Functor::map`] or [`RefFunctor::ref_map`]
281 /// based on the closure's argument type:
282 ///
283 /// - If the closure takes owned values (`Fn(A) -> B`) and the container is
284 /// owned, dispatches to [`Functor::map`].
285 /// - If the closure takes references (`Fn(&A) -> B`) and the container is
286 /// borrowed (`&fa`), dispatches to [`RefFunctor::ref_map`].
287 ///
288 /// The `Marker` and `FA` type parameters are inferred automatically by the
289 /// compiler from the closure's argument type and the container argument.
290 /// Callers write `map::<Brand, _, _, _, _>(...)` and never need to specify
291 /// `Marker` or `FA` explicitly.
292 ///
293 /// The dispatch is resolved at compile time with no runtime cost.
294 #[document_signature]
295 ///
296 #[document_type_parameters(
297 "The lifetime of the values.",
298 "The brand of the functor.",
299 "The type of the value(s) inside the functor.",
300 "The type of the result(s) of applying the function.",
301 "The container type (owned or borrowed), inferred from the argument.",
302 "Dispatch marker type, inferred automatically."
303 )]
304 ///
305 #[document_parameters(
306 "The function to apply to the value(s) inside the functor.",
307 "The functor instance (owned for Val, borrowed for Ref)."
308 )]
309 ///
310 #[document_returns(
311 "A new functor instance containing the result(s) of applying the function."
312 )]
313 ///
314 #[document_examples]
315 ///
316 /// ```
317 /// use fp_library::{
318 /// brands::*,
319 /// functions::explicit::*,
320 /// types::*,
321 /// };
322 ///
323 /// // Owned: dispatches to Functor::map
324 /// let y = map::<OptionBrand, _, _, _, _>(|x: i32| x * 2, Some(5));
325 /// assert_eq!(y, Some(10));
326 ///
327 /// // By-ref: dispatches to RefFunctor::ref_map
328 /// let lazy = RcLazy::pure(10);
329 /// let mapped = map::<LazyBrand<RcLazyConfig>, _, _, _, _>(|x: &i32| *x * 2, &lazy);
330 /// assert_eq!(*mapped.evaluate(), 20);
331 /// ```
332 pub fn map<'a, Brand: Kind_cdc7cd43dac7585f, A: 'a, B: 'a, FA, Marker>(
333 f: impl FunctorDispatch<'a, Brand, A, B, FA, Marker>,
334 fa: FA,
335 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
336 f.dispatch(fa)
337 }
338 }
339}
340
341pub use inner::*;