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