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}