Skip to main content

fp_library/classes/
functor.rs

1//! Types that can be mapped over, allowing functions to be applied to values within a context.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! 	brands::*,
8//! 	functions::*,
9//! };
10//!
11//! let x = Some(5);
12//! let y = map::<OptionBrand, _, _>(|i| i * 2, x);
13//! assert_eq!(y, Some(10));
14//! ```
15
16#[fp_macros::document_module]
17mod inner {
18	use {
19		crate::kinds::*,
20		fp_macros::*,
21	};
22
23	/// A type class for types that can be mapped over.
24	///
25	/// A `Functor` represents a context or container that allows functions to be applied
26	/// to values within that context without altering the structure of the context itself.
27	///
28	/// ### Hierarchy Unification
29	///
30	/// This trait now inherits from [`Kind_cdc7cd43dac7585f`], ensuring that all functor
31	/// contexts satisfy the strict lifetime requirements where the type argument must
32	/// outlive the context's application lifetime.
33	///
34	/// By explicitly requiring that the type parameter outlives the application lifetime `'a`,
35	/// we provide the compiler with the necessary guarantees to handle trait objects
36	/// (like `dyn Fn`) commonly used in functor implementations. This resolves potential
37	/// E0310 errors where the compiler cannot otherwise prove that captured variables in
38	/// closures satisfy the required lifetime bounds.
39	///
40	/// ### Laws
41	///
42	/// `Functor` instances must satisfy the following laws:
43	/// * Identity: `map(identity, fa) = fa`.
44	/// * Composition: `map(compose(f, g), fa) = map(f, map(g, fa))`.
45	pub trait Functor: Kind_cdc7cd43dac7585f {
46		/// Maps a function over the values in the functor context.
47		///
48		/// This method applies a function to the value(s) inside the functor context, producing a new functor context with the transformed value(s).
49		#[document_signature]
50		///
51		#[document_type_parameters(
52			"The lifetime of the values.",
53			"The type of the value(s) inside the functor.",
54			"The type of the result(s) of applying the function."
55		)]
56		///
57		#[document_parameters(
58			"The function to apply to the value(s) inside the functor.",
59			"The functor instance containing the value(s)."
60		)]
61		///
62		#[document_returns(
63			"A new functor instance containing the result(s) of applying the function."
64		)]
65		///
66		#[document_examples]
67		///
68		/// ```
69		/// use fp_library::{
70		/// 	brands::*,
71		/// 	functions::*,
72		/// };
73		///
74		/// let x = Some(5);
75		/// let y = map::<OptionBrand, _, _>(|i| i * 2, x);
76		/// assert_eq!(y, Some(10));
77		/// ```
78		fn map<'a, A: 'a, B: 'a>(
79			f: impl Fn(A) -> B + 'a,
80			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
81		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>);
82	}
83
84	/// Maps a function over the values in the functor context.
85	///
86	/// Free function version that dispatches to [the type class' associated function][`Functor::map`].
87	#[document_signature]
88	///
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	)]
95	///
96	#[document_parameters(
97		"The function to apply to the value(s) inside the functor.",
98		"The functor instance containing the value(s)."
99	)]
100	///
101	#[document_returns("A new functor instance containing the result(s) of applying the function.")]
102	///
103	#[document_examples]
104	///
105	/// ```
106	/// use fp_library::{
107	/// 	brands::*,
108	/// 	functions::*,
109	/// };
110	///
111	/// let x = Some(5);
112	/// let y = map::<OptionBrand, _, _>(|i| i * 2, x);
113	/// assert_eq!(y, Some(10));
114	/// ```
115	pub fn map<'a, Brand: Functor, A: 'a, B: 'a>(
116		f: impl Fn(A) -> B + 'a,
117		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
118	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
119		Brand::map(f, fa)
120	}
121}
122
123pub use inner::*;