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