|
|
@ -0,0 +1,42 @@ |
|
|
|
#![feature(conservative_impl_trait)]
|
|
|
|
#![feature(box_syntax)]
|
|
|
|
|
|
|
|
/// The identity function. Maps a categorical object onto itself.
|
|
|
|
///
|
|
|
|
/// # Haskell
|
|
|
|
/// ```text
|
|
|
|
/// id :: a -> a
|
|
|
|
/// id x = x
|
|
|
|
/// ```
|
|
|
|
pub fn id<T>(x: T) -> T { x }
|
|
|
|
|
|
|
|
/// The composition function. Chains two functions together.
|
|
|
|
///
|
|
|
|
/// # Haskell
|
|
|
|
/// ```text
|
|
|
|
/// compose :: (b -> c) -> (a -> b) -> (a -> c)
|
|
|
|
/// compose f g = λx -> f $ g x
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// # Notes
|
|
|
|
/// I don't like that this takes `Box`ed functions. Rust doesn't like parameterizing on functions
|
|
|
|
/// with generic arguments, even if those arguments are sized
|
|
|
|
/// (`rustc` complains that `std::ops::Fn(A) -> B + 'static` does not have a constant size known at compile-time)
|
|
|
|
pub fn compose<A, B, C>(f: Box<Fn(B) -> C>, g: Box<Fn(A) -> B>) -> impl Fn(A) -> C {
|
|
|
|
move |x| { f(g(x)) }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn compose_smoketest() {
|
|
|
|
fn f(x: f64) -> f64 { x.powi(2) }
|
|
|
|
fn g(x: f64) -> f64 { x + 3. }
|
|
|
|
let h = compose(box g, box f);
|
|
|
|
|
|
|
|
assert_eq!(h(3.), g(f(3.)));
|
|
|
|
}
|
|
|
|
}
|