qubit_function/functions/bi_mutating_function/fn_bi_mutating_function_ops.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10// qubit-style: allow explicit-imports
11//! Defines the `FnBiMutatingFunctionOps` public type.
12
13use super::{
14 BiPredicate,
15 BoxBiMutatingFunction,
16 BoxConditionalBiMutatingFunction,
17};
18
19// ============================================================================
20// FnBiMutatingFunctionOps - Extension trait for Fn(&mut T, &mut U) -> R bi-functions
21// ============================================================================
22
23/// Extension trait for closures implementing `Fn(&mut T, &mut U) -> R`
24///
25/// Provides composition methods (`and_then`, `when`) for bi-mutating-function
26/// closures and function pointers without requiring explicit wrapping in
27/// `BoxBiMutatingFunction`.
28///
29/// This trait is automatically implemented for all closures and function
30/// pointers that implement `Fn(&mut T, &mut U) -> R`.
31///
32/// # Design Rationale
33///
34/// While closures automatically implement `BiMutatingFunction<T, U, R>` through
35/// blanket implementation, they don't have access to instance methods like
36/// `and_then` and `when`. This extension trait provides those methods,
37/// returning `BoxBiMutatingFunction` for maximum flexibility.
38///
39/// # Examples
40///
41/// ## Chain composition with and_then
42///
43/// ```rust
44/// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
45///
46/// let swap_and_sum = |x: &mut i32, y: &mut i32| {
47/// let temp = *x;
48/// *x = *y;
49/// *y = temp;
50/// *x + *y
51/// };
52/// let double = |x: &i32| x * 2;
53///
54/// let composed = swap_and_sum.and_then(double);
55/// let mut a = 3;
56/// let mut b = 5;
57/// assert_eq!(composed.apply(&mut a, &mut b), 16); // (5 + 3) * 2 = 16
58/// ```
59///
60/// ## Conditional execution with when
61///
62/// ```rust
63/// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
64///
65/// let swap_and_sum = |x: &mut i32, y: &mut i32| {
66/// let temp = *x;
67/// *x = *y;
68/// *y = temp;
69/// *x + *y
70/// };
71/// let multiply = |x: &mut i32, y: &mut i32| {
72/// *x *= *y;
73/// *x
74/// };
75///
76/// let conditional = swap_and_sum.when(|x: &i32, y: &i32| *x > 0 && *y > 0).or_else(multiply);
77///
78/// let mut a = 5;
79/// let mut b = 3;
80/// assert_eq!(conditional.apply(&mut a, &mut b), 8); // swap_and_sum: (3 + 5)
81///
82/// let mut a = -5;
83/// let mut b = 3;
84/// assert_eq!(conditional.apply(&mut a, &mut b), -15); // multiply: (-5 * 3)
85/// ```
86///
87pub trait FnBiMutatingFunctionOps<T, U, R>: Fn(&mut T, &mut U) -> R + Sized {
88 /// Chain composition - applies self first, then after
89 ///
90 /// Creates a new bi-mutating-function that applies this bi-mutating-function first,
91 /// then applies the after function to the result. Consumes self and
92 /// returns a `BoxBiMutatingFunction`.
93 ///
94 /// # Type Parameters
95 ///
96 /// * `S` - The output type of the after function
97 /// * `F` - The type of the after function (must implement Function<R, S>)
98 ///
99 /// # Parameters
100 ///
101 /// * `after` - The function to apply after self. **Note: This parameter
102 /// is passed by value and will transfer ownership.** If you need to
103 /// preserve the original function, clone it first (if it implements
104 /// `Clone`). Can be:
105 /// - A closure: `|x: R| -> S`
106 /// - A function pointer: `fn(R) -> S`
107 /// - A `BoxFunction<R, S>`
108 /// - An `RcFunction<R, S>`
109 /// - An `ArcFunction<R, S>`
110 /// - Any type implementing `Function<R, S>`
111 ///
112 /// # Returns
113 ///
114 /// A new `BoxBiMutatingFunction<T, U, S>` representing the composition
115 ///
116 /// # Examples
117 ///
118 /// ## Direct value passing (ownership transfer)
119 ///
120 /// ```rust
121 /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
122 /// BoxFunction, Function};
123 ///
124 /// let swap = |x: &mut i32, y: &mut i32| {
125 /// let temp = *x;
126 /// *x = *y;
127 /// *y = temp;
128 /// *x + *y
129 /// };
130 /// let to_string = BoxFunction::new(|x: &i32| x.to_string());
131 ///
132 /// // to_string is moved here
133 /// let composed = swap.and_then(to_string);
134 /// let mut a = 20;
135 /// let mut b = 22;
136 /// assert_eq!(composed.apply(&mut a, &mut b), "42");
137 /// // to_string.apply(10); // Would not compile - moved
138 /// ```
139 ///
140 /// ## Preserving original with clone
141 ///
142 /// ```rust
143 /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
144 /// Function, RcFunction};
145 ///
146 /// let swap = |x: &mut i32, y: &mut i32| {
147 /// let temp = *x;
148 /// *x = *y;
149 /// *y = temp;
150 /// *x + *y
151 /// };
152 /// let to_string = RcFunction::new(|x: &i32| x.to_string());
153 ///
154 /// // Clone to preserve original
155 /// let composed = swap.and_then(to_string.clone());
156 /// let mut a = 20;
157 /// let mut b = 22;
158 /// assert_eq!(composed.apply(&mut a, &mut b), "42");
159 ///
160 /// // Original still usable
161 /// assert_eq!(to_string.apply(&10), "10");
162 /// ```
163 fn and_then<S, F>(self, after: F) -> BoxBiMutatingFunction<T, U, S>
164 where
165 Self: 'static,
166 S: 'static,
167 F: crate::functions::function::Function<R, S> + 'static,
168 T: 'static,
169 U: 'static,
170 R: 'static,
171 {
172 BoxBiMutatingFunction::new(move |t: &mut T, u: &mut U| after.apply(&self(t, u)))
173 }
174
175 /// Creates a conditional bi-mutating-function
176 ///
177 /// Returns a bi-mutating-function that only executes when a bi-predicate is
178 /// satisfied. You must call `or_else()` to provide an alternative
179 /// bi-mutating-function for when the condition is not satisfied.
180 ///
181 /// # Parameters
182 ///
183 /// * `predicate` - The condition to check. **Note: This parameter is passed
184 /// by value and will transfer ownership.** If you need to preserve the
185 /// original bi-predicate, clone it first (if it implements `Clone`).
186 /// Can be:
187 /// - A closure: `|x: &mut T, y: &mut U| -> bool`
188 /// - A function pointer: `fn(&mut T, &mut U) -> bool`
189 /// - A `BoxBiPredicate<T, U>`
190 /// - An `RcBiPredicate<T, U>`
191 /// - An `ArcBiPredicate<T, U>`
192 /// - Any type implementing `BiPredicate<T, U>`
193 ///
194 /// # Returns
195 ///
196 /// Returns `BoxConditionalBiMutatingFunction<T, U, R>`
197 ///
198 /// # Examples
199 ///
200 /// ## Basic usage with or_else
201 ///
202 /// ```rust
203 /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps};
204 ///
205 /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
206 /// let temp = *x;
207 /// *x = *y;
208 /// *y = temp;
209 /// *x + *y
210 /// };
211 /// let multiply = |x: &mut i32, y: &mut i32| {
212 /// *x *= *y;
213 /// *x
214 /// };
215 /// let conditional = swap_and_sum.when(|x: &i32, y: &i32| *x > 0)
216 /// .or_else(multiply);
217 ///
218 /// let mut a = 5;
219 /// let mut b = 3;
220 /// assert_eq!(conditional.apply(&mut a, &mut b), 8);
221 /// ```
222 ///
223 /// ## Preserving bi-predicate with clone
224 ///
225 /// ```rust
226 /// use qubit_function::{BiMutatingFunction, FnBiMutatingFunctionOps,
227 /// RcBiPredicate};
228 ///
229 /// let swap_and_sum = |x: &mut i32, y: &mut i32| {
230 /// let temp = *x;
231 /// *x = *y;
232 /// *y = temp;
233 /// *x + *y
234 /// };
235 /// let both_positive = RcBiPredicate::new(|x: &i32, y: &i32|
236 /// *x > 0 && *y > 0);
237 ///
238 /// // Clone to preserve original bi-predicate
239 /// let conditional = swap_and_sum.when(both_positive.clone())
240 /// .or_else(|x: &mut i32, y: &mut i32| *x * *y);
241 ///
242 /// let mut a = 5;
243 /// let mut b = 3;
244 /// assert_eq!(conditional.apply(&mut a, &mut b), 8);
245 ///
246 /// // Original bi-predicate still usable
247 /// use qubit_function::BiPredicate;
248 /// let test_a = 5;
249 /// let test_b = 3;
250 /// assert!(both_positive.test(&test_a, &test_b));
251 /// ```
252 fn when<P>(self, predicate: P) -> BoxConditionalBiMutatingFunction<T, U, R>
253 where
254 Self: 'static,
255 P: BiPredicate<T, U> + 'static,
256 T: 'static,
257 U: 'static,
258 R: 'static,
259 {
260 BoxBiMutatingFunction::new(self).when(predicate)
261 }
262}
263
264/// Blanket implementation of FnBiMutatingFunctionOps for all closures
265///
266/// Automatically implements `FnBiMutatingFunctionOps<T, U, R>` for any type that
267/// implements `Fn(&mut T, &mut U) -> R`.
268///
269impl<T, U, R, F> FnBiMutatingFunctionOps<T, U, R> for F where F: Fn(&mut T, &mut U) -> R {}