currycompose/
lib.rs

1#![feature(tuple_trait)]
2#![feature(unboxed_closures)]
3#![feature(fn_traits)]
4#![feature(generic_const_exprs)]
5#![feature(const_trait_impl)]
6
7//! https://en.wikipedia.org/wiki/Function_composition
8//! 
9//! A crate providing a trait for performing currying (and non-currying) function-composition in rust.
10//! 
11//! Non-currying composition:
12//! h(x) = g ∘ f = g(f(x))
13//! 
14//! Currying composition:
15//! h(..., x) = g ∘ f = g(f(x), ...)
16//! 
17//! When currying, arguments of the function being curried with (f) is moved to the end of the argument-list
18//! 
19//! Both operands must implement FnOnce. If both implement FnMut or Fn, the resulting composition will also implement these traits.
20//! 
21//! g must also have one or more argument, where the first argument type equals the return type of f.
22//! 
23//! ```rust
24//! #![feature(generic_const_exprs)]
25//! 
26//! use currycompose::*;
27//! 
28//! // g ∘ f
29//! // where
30//! // g :: f32 -> f32
31//! // f :: u8 -> f32
32//! // g ∘ f :: u8 -> f32
33//! let g = |x: f32| x*x;
34//! let f = |x: u8| x as f32;
35//! 
36//! let gf = g.compose(f);
37//! 
38//! let x = 1;
39//! 
40//! assert_eq!(gf(x), g(f(x)));
41//! 
42//! // g ∘ f
43//! // where
44//! // g :: f32 -> f32 -> f32
45//! // f :: u8 -> f32
46//! // g ∘ f :: f32 -> u8 -> f32
47//! let g = |x: f32, y: f32| x + y;
48//! let f = gf;
49//! 
50//! let gf = g.compose(f);
51//! 
52//! let x = 1;
53//! let y = 1.0;
54//! 
55//! // note here the argument x has been shifted to the end of the args in gf
56//! assert_eq!(gf(y, x), g(f(x), y));
57//! 
58//! // g ∘ f ∘ f
59//! // where
60//! // g :: f32 -> f32 -> f32
61//! // f :: u8 -> f32
62//! // g ∘ f ∘ f :: u8 -> u8 -> f32
63//! let gff = gf.compose(f);
64//! 
65//! let x = 1;
66//! let y = 1;
67//! 
68//! assert_eq!(gff(x, y), g(f(x), f(y)));
69//! ```
70
71use std::marker::{Tuple, PhantomData};
72
73use tupleops::{ConcatTuples, TupleConcat, concat_tuples, TupleLength, TupleUnprepend, Head, Tail};
74
75use tuple_split::{TupleSplit, SplitInto};
76
77/// https://en.wikipedia.org/wiki/Function_composition
78/// 
79/// Trait for composing two functions (currying and non-curring composition)
80/// 
81/// Non-currying composition:
82/// h(x) = g ∘ f = g(f(x))
83/// 
84/// Currying composition:
85/// h(..., x) = g ∘ f = g(f(x), ...)
86/// 
87/// When currying, arguments of the function being curried with (f) is moved to the end of the argument-list
88/// 
89/// Both operands must implement FnOnce. If both implement FnMut or Fn, the resulting composition will also implement these traits.
90/// 
91/// g must also have one or more argument, where the first argument type equals the return type of f.
92/// 
93/// ```rust
94/// #![feature(generic_const_exprs)]
95/// 
96/// use currycompose::*;
97/// 
98/// // g ∘ f
99/// // where
100/// // g :: f32 -> f32
101/// // f :: u8 -> f32
102/// // g ∘ f :: u8 -> f32
103/// let g = |x: f32| x*x;
104/// let f = |x: u8| x as f32;
105/// 
106/// let gf = g.compose(f);
107/// 
108/// let x = 1;
109/// 
110/// assert_eq!(gf(x), g(f(x)));
111/// 
112/// // g ∘ f
113/// // where
114/// // g :: f32 -> f32 -> f32
115/// // f :: u8 -> f32
116/// // g ∘ f :: f32 -> u8 -> f32
117/// let g = |x: f32, y: f32| x + y;
118/// let f = gf;
119/// 
120/// let gf = g.compose(f);
121/// 
122/// let x = 1;
123/// let y = 1.0;
124/// 
125/// // note here the argument x has been shifted to the end of the args in gf
126/// assert_eq!(gf(y, x), g(f(x), y));
127/// 
128/// // g ∘ f ∘ f
129/// // where
130/// // g :: f32 -> f32 -> f32
131/// // f :: u8 -> f32
132/// // g ∘ f ∘ f :: u8 -> u8 -> f32
133/// let gff = gf.compose(f);
134/// 
135/// let x = 1;
136/// let y = 1;
137/// 
138/// assert_eq!(gff(x, y), g(f(x), f(y)));
139/// ```
140#[const_trait]
141pub trait Compose<F, XG, XF>: Sized
142{
143    /// Composing two functions
144    /// 
145    /// h(x) = g ∘ f = g(f(x))
146    fn compose(self, with: F) -> Composition<Self, F, XG, XF>;
147}
148
149impl<G, F, XG, XF> const Compose<F, XG, XF> for G
150where
151    XG: Tuple + TupleUnprepend<XG>,
152    XF: Tuple,
153    Self: FnOnce<XG>,
154    F: FnOnce<XF, Output = Head<XG>>,
155    (Tail<XG>, XF): TupleConcat<Tail<XG>, XF>,
156    ConcatTuples<Tail<XG>, XF>: Tuple,
157    Composition<Self, F, XG, XF>: FnOnce<ConcatTuples<Tail<XG>, XF>>
158{
159    fn compose(self, with: F) -> Composition<Self, F, XG, XF>
160    {
161        Composition {
162            g: self,
163            f: with,
164            phantom: PhantomData
165        }
166    }
167}
168
169/// A struct representing a function composed with another.
170/// 
171/// When calling the composition as a function, the leftover arguments of the composition function come first (if curried), then the arguments of the function being composed with.
172/// 
173/// ```rust
174/// #![feature(generic_const_exprs)]
175/// 
176/// use currycompose::*;
177/// 
178/// // g ∘ f
179/// // where
180/// // g :: f32 -> f32
181/// // f :: u8 -> f32
182/// // g ∘ f :: u8 -> f32
183/// let g = |x: f32| x*x;
184/// let f = |x: u8| x as f32;
185/// 
186/// let gf = g.compose(f);
187/// 
188/// let x = 1;
189/// 
190/// assert_eq!(gf(x), g(f(x)));
191/// 
192/// // g ∘ f
193/// // where
194/// // g :: f32 -> f32 -> f32
195/// // f :: u8 -> f32
196/// // g ∘ f :: f32 -> u8 -> f32
197/// let g = |x: f32, y: f32| x + y;
198/// let f = gf;
199/// 
200/// let gf = g.compose(f);
201/// 
202/// let x = 1;
203/// let y = 1.0;
204/// 
205/// // note here the argument x has been shifted to the end of the args in gf
206/// assert_eq!(gf(y, x), g(f(x), y));
207/// 
208/// // g ∘ f ∘ f
209/// // where
210/// // g :: f32 -> f32 -> f32
211/// // f :: u8 -> f32
212/// // g ∘ f ∘ f :: u8 -> u8 -> f32
213/// let gff = gf.compose(f);
214/// 
215/// let x = 1;
216/// let y = 1;
217/// 
218/// assert_eq!(gff(x, y), g(f(x), f(y)));
219/// ```
220#[derive(Clone, Copy, Debug)]
221pub struct Composition<G, F, XG, XF>
222{
223    g: G,
224    f: F,
225    phantom: PhantomData<(XG, XF)>,
226}
227
228impl<G, F, XG, XF> FnOnce<ConcatTuples<Tail<XG>, XF>> for Composition<G, F, XG, XF>
229where
230    XG: Tuple + TupleUnprepend<XG>,
231    XF: Tuple,
232    G: FnOnce<XG>,
233    F: FnOnce<XF, Output = Head<XG>>,
234    (Tail<XG>, XF): TupleConcat<Tail<XG>, XF>,
235    ConcatTuples<Tail<XG>, XF>: Tuple + SplitInto<Tail<XG>, XF>,
236    [(); <Tail<XG> as TupleLength>::LENGTH]:,
237    ((F::Output,), Tail<XG>): TupleConcat<(F::Output,), Tail<XG>, Type = XG>
238{
239    type Output = <G as FnOnce<XG>>::Output;
240
241    extern "rust-call" fn call_once(self, args: ConcatTuples<Tail<XG>, XF>) -> Self::Output
242    {
243        let (left, right): (Tail<XG>, XF) = args.split_tuple();
244        self.g.call_once(concat_tuples((self.f.call_once(right),), left))
245    }
246}
247
248impl<G, F, XG, XF> FnMut<ConcatTuples<Tail<XG>, XF>> for Composition<G, F, XG, XF>
249where
250    XG: Tuple + TupleUnprepend<XG>,
251    XF: Tuple,
252    G: FnMut<XG>,
253    F: FnMut<XF, Output = Head<XG>>,
254    (Tail<XG>, XF): TupleConcat<Tail<XG>, XF>,
255    ConcatTuples<Tail<XG>, XF>: Tuple + SplitInto<Tail<XG>, XF>,
256    [(); <Tail<XG> as TupleLength>::LENGTH]:,
257    ((F::Output,), Tail<XG>): TupleConcat<(F::Output,), Tail<XG>, Type = XG>
258{
259    extern "rust-call" fn call_mut(&mut self, args: ConcatTuples<Tail<XG>, XF>) -> Self::Output
260    {
261        let (left, right) = args.split_tuple();
262        self.g.call_mut(concat_tuples((self.f.call_mut(right),), left))
263    }
264}
265
266impl<G, F, XG, XF> Fn<ConcatTuples<Tail<XG>, XF>> for Composition<G, F, XG, XF>
267where
268    XG: Tuple + TupleUnprepend<XG>,
269    XF: Tuple,
270    G: Fn<XG>,
271    F: Fn<XF, Output = Head<XG>>,
272    (Tail<XG>, XF): TupleConcat<Tail<XG>, XF>,
273    ConcatTuples<Tail<XG>, XF>: Tuple + SplitInto<Tail<XG>, XF>,
274    [(); <Tail<XG> as TupleLength>::LENGTH]:,
275    ((F::Output,), Tail<XG>): TupleConcat<(F::Output,), Tail<XG>, Type = XG>
276{
277    extern "rust-call" fn call(&self, args: ConcatTuples<Tail<XG>, XF>) -> Self::Output
278    {
279        let (left, right) = args.split_tuple();
280        self.g.call(concat_tuples((self.f.call(right),), left))
281    }
282}