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