Skip to main content

qubit_function/functions/bi_function/
fn_bi_function_ops.rs

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