Function ctrs::compose [−][src]
pub fn compose<F: 'static, G: 'static, Fv, Gv, V>(
f: F,
g: G
) -> Box<Fn(Fv) -> V> where
F: Fn(Fv) -> Gv,
G: Fn(Gv) -> V,
Composition is the heart of categorical computation.
Overview
Our definition of composition may appear confusing at first, but let's break it down. We start by defining generic
types for our two input functions. These areF and G, respectively. These have a 'static lifetime because we
have to ensure that the borrow checker does not let these types out of scope before computation has finished. Next,
we have types Fv and Gv, which represent the types for the return values for each of the functions F and G.
Finally, we have our output type V, which is the result we want. We pass the functions F and G as parameters f
and g.
Still with me?
Next, the return value is a Box of the generic Fn type that takes an Fv to a V. We have to box the return
value because we do not know how much size it could occupy on the stack (thus we allocate to the heap). Finally, we
implement trait bounds on F and G, specifying how the chain should compose: F takes an Fv to a Gv, and then G takes a
Gv to V.
Phew! 😅
Let's now see how this looks in practice using an example.
Example
use ctrs::{id, compose}; // Let's first define a trivial incrementer function. fn inc(x: i32) -> i32 { x + 1 } // and cover our bases by confirming inc works as expected. let x = 1; assert_eq!(inc(x), 2); // Since we are composing functions on a given value, the syntax is // compose(A, B)(V). Knowing this, our passing test looks like: assert_eq!(compose(id, inc)(1), 2);
We can extend this idea! Let's take the situation where we've also defined an admittedly contrived double
function, and want to compose its behavior with our existing incrementer. Mathematicians sometimes call the
composition operator one might find in Haskell after, and understanding the way in which the function associates
is indeed g after f.
fn double(x: i32) -> i32 { x * 2 } let x = 1; assert_eq!(compose(inc, double)(1), 4);