1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Applying functions within a context to values within a context, without an identity element.
//!
//! ### Examples
//!
//! ```
//! use fp_library::{
//! brands::*,
//! classes::*,
//! functions::*,
//! };
//!
//! let f = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
//! let x = Some(5);
//! let y = apply(f, x);
//! assert_eq!(y, Some(10));
//! ```
#[fp_macros::document_module]
mod inner {
use {
crate::{
classes::*,
kinds::*,
},
fp_macros::*,
};
/// A type class for applying functions within a context to values within a context.
///
/// `Semiapplicative` provides the ability to apply functions that are themselves
/// wrapped in a context to values that are also wrapped in a context.
///
/// ### Laws
///
/// `Semiapplicative` instances must satisfy the following law:
/// * Associative composition: `apply(apply(map(|f| |g| compose(f, g), u), v), w) = apply(u, apply(v, w))`.
#[document_examples]
///
/// Associative composition for [`Option`]:
///
/// ```
/// use fp_library::{
/// brands::*,
/// classes::*,
/// functions::{
/// explicit::map,
/// *,
/// },
/// };
///
/// let u = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x + 1));
/// let v = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
/// let w = Some(5i32);
///
/// // Right side: apply(u, apply(v, w))
/// let right = apply(u.clone(), apply(v.clone(), w));
///
/// // Left side: apply(apply(map(|f| |g| compose(f, g), u), v), w)
/// // Step 1: map the composition combinator over u
/// let compose_u = map::<OptionBrand, _, _, _, _>(
/// |u_fn: std::rc::Rc<dyn Fn(i32) -> i32>| {
/// lift_fn_new::<RcFnBrand, _, _>(move |v_fn: std::rc::Rc<dyn Fn(i32) -> i32>| {
/// let u_fn = u_fn.clone();
/// lift_fn_new::<RcFnBrand, _, _>(move |x: i32| u_fn(v_fn(x)))
/// })
/// },
/// u,
/// );
/// // Step 2: apply to v, then to w
/// let composed = apply(compose_u, v);
/// let left = apply(composed, w);
///
/// assert_eq!(left, right);
/// assert_eq!(left, Some(11)); // (5 * 2) + 1
/// ```
pub trait Semiapplicative: Lift + Functor {
/// Applies a function within a context to a value within a context.
///
/// This method applies a function wrapped in a context to a value wrapped in a context.
///
/// **Important**: This operation requires type erasure for heterogeneous functions.
/// When a container (like `Vec`) holds multiple different closures, they must be
/// type-erased via `Rc<dyn Fn>` or `Arc<dyn Fn>` because each Rust closure is a
/// distinct anonymous type.
#[document_signature]
///
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the cloneable function wrapper.",
"The type of the input value.",
"The type of the output value."
)]
///
#[document_parameters(
"The context containing the function(s).",
"The context containing the value(s)."
)]
///
#[document_returns(
"A new context containing the result(s) of applying the function(s) to the value(s)."
)]
#[document_examples]
///
/// ```
/// use fp_library::{
/// brands::*,
/// classes::*,
/// functions::*,
/// };
///
/// let f = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
/// let x = Some(5);
/// let y = apply(f, x);
/// assert_eq!(y, Some(10));
/// ```
fn apply<'a, FnBrand: 'a + CloneFn, A: 'a + Clone, B: 'a>(
ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, A, B>>),
fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>);
}
/// Applies a function within a context to a value within a context.
///
/// Free function version that dispatches to [the type class' associated function][`Semiapplicative::apply`].
#[document_signature]
///
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the cloneable function wrapper.",
"The brand of the context.",
"The type of the input value.",
"The type of the output value."
)]
///
#[document_parameters(
"The context containing the function(s).",
"The context containing the value(s)."
)]
///
#[document_returns(
"A new context containing the result(s) of applying the function(s) to the value(s)."
)]
#[document_examples]
///
/// ```
/// use fp_library::{
/// brands::*,
/// classes::*,
/// functions::*,
/// };
///
/// let f = Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2));
/// let x = Some(5);
/// let y = apply(f, x);
/// assert_eq!(y, Some(10));
/// ```
pub fn apply<'a, FnBrand: 'a + CloneFn, Brand: Semiapplicative, A: 'a + Clone, B: 'a>(
ff: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneFn>::Of<'a, A, B>>),
fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
Brand::apply::<FnBrand, A, B>(ff, fa)
}
}
pub use inner::*;