lambda 0.0.1

Utilities and macros for functional programming with closures
#![crate_name="lambda"]
#![crate_type="lib"]

//! This crate provides utilities and macros for working with closures
//! and Morphisms, particularly with respect to functional
//! programming.

#![feature(default_type_params)]
#![feature(macro_rules)]
#![feature(non_ascii_idents)]

extern crate morphism;

#[allow(unused_imports)]
use morphism::{
    Morphism
};

/// Create a new `Morphism` with a closure expression as the initial
/// component of the closure chain.
///
/// # Example
///
/// ```
/// assert_eq!(lambda!(| x | 42u * x).o(| x | (x, x))(1u), (42u, 42u))
/// ```
#[macro_export]
macro_rules! lambda {
    ( $closure:expr ) => { Morphism::new().tail($closure) }
}

/// Alias for `lambda!`.
///
/// # Example
///
/// ```
/// assert_eq!(λ!(| x | 42u * x).o(| x | (x, x))(1u), (42u, 42u))
/// ```
#[macro_export]
macro_rules! λ {
    ( $($raw:tt)* ) => { lambda!( $($raw)* ) }
}

/// A trait extending `Morphism` with some utility methods.
pub trait MorphismExt<'a, A, B> {
    /// An alias for `Morphism::tail`.
    ///
    /// # Example
    ///
    /// ```
    /// let f = λ!(| x | (0u + x,))
    ///     .o(| (x,) | (0u + x, 1u + x))
    ///     .o(| (x, y) | (0u + x, 1u + y, 2u + y))
    ///     .o(| (x, y, z) | (Some(z), Ok::<_, bool>(y), x.to_f64().unwrap() + 1.5f64));
    ///
    /// assert_eq!(f(0), (Some(3), Ok(2), 1.5f64));
    /// ```
    fn o<C, F: 'a>(self, f: F) -> Morphism<'a, A, C>
        where
        F: Fn(B) -> C;
}

impl<'a, A, B> MorphismExt<'a, A, B> for Morphism<'a, A, B> {
    #[inline(always)]
    fn o<C, F: 'a>(self, f : F) -> Morphism<'a, A, C>
        where
        F: Fn(B) -> C
    {
        self.tail(f)
    }
}

#[cfg(test)]
mod tests {
    use morphism::Morphism;
    use super::MorphismExt;

    #[test]
    fn test_example_lambda_macro() {
        assert_eq!(lambda!(| x | 42u * x).o(| x | (x, x))(1u), (42u, 42u));
    }

    #[test]
    fn test_example_λ_macro() {
        assert_eq!(λ!(| x | 42u * x).o(| x | (x, x))(1u), (42u, 42u));
    }

    #[test]
    fn test_o() {
        let f = λ!(| x | (0u + x,))
            .o(| (x,) | (0u + x, 1u + x))
            .o(| (x, y) | (0u + x, 1u + y, 2u + y))
            .o(| (x, y, z) | (Some(z), Ok::<_, bool>(y), x.to_f64().unwrap() + 1.5f64));
        assert_eq!(f(0), (Some(3), Ok(2), 1.5f64));
    }

    #[test]
    fn test_example_readme() {
        use std::iter::AdditiveIterator;
        use std::num::Float;

        let f =
            λ!(| x | 2u64 * x)
            .o(| x | 1.5f64 + x.to_f64().unwrap())
            .o(| x | x.floor().to_u64().unwrap());
        
        let res = range(0u64, 9).map(f).sum();

        assert_eq!(res, 81);
    }
}