functional 0.0.7

Functional traits

// use std::rc::Rc;
// use std::sync::Arc;

/// Equality trait
/// It is widely used in complex type traits to declare equalty between any types
/// Same is implemented for all types
/// No others implementations alowed
pub trait Same {
    type Type;
    fn same_cast(self) -> Self::Type;
}

impl<T> Same for T {
    type Type = T;
    fn same_cast(self) -> T {
        self
    }
}

/// Generic types can implement this trait
/// providing an ability to extract first generic parameter.
pub trait Generic1 {
    type Unit: ?Sized;
    type Type: ?Sized;
}

/// Generic types can implement this trait
/// providing an ability to change first generic parameter.
/// Associated `Type` must have same HKT.
/// There are few laws:
/// `<<X As Rebind1<T>::Type as Generic1>::Type == T`
/// `<X As Rebind1<<X as Generic1>::Type>::Type == X`
pub trait Rebind1<Y: ?Sized>: Generic1<Unit=Self> {
    type Type: Generic1<Unit=Self, Type=Y>;
}

/// Generic types with at least two parameters can implement this trait
/// providing an ability to extract second generic parameter.
pub trait Generic2: Generic1 {
    type Unit: ?Sized;
    type Type: ?Sized;
}

/// Generic types with at least two parameters can implement this trait
/// providing an ability to change second generic parameter.
/// Associated `Type` must have same HKT.
/// There are few laws:
/// `<<X As Rebind2<T>::Type as Generic2>::Type == T`
/// `<X As Rebind2<<X as Generic2>::Type>::Type == X`
pub trait Rebind2<T: ?Sized>: Generic2<Unit=Self> {
    type Type: Generic2<Unit=Self, Type=T>;
}

#[macro_export]
/// Implementation of Generic1 and Rebind1 for HKTs with 1 parameter
macro_rules! declare_generic1 {
    ($x:ident) => {
        impl<T> Generic1 for $x<T> {
            type Type = T;
            type Unit = $x<()>;
        }
        impl<T> Rebind1<T> for $x<()> {
            type Type = $x<T>;
        }
    };
}

#[macro_export]
macro_rules! generic1 {
    ($x:ty) => { <$x as Generic1>::Type }
}

#[macro_export]
macro_rules! can_rebind1 {
    ($x:ty, $y:ty) => { <$x as Generic1>::Unit: Rebind1<$y> }
}

#[macro_export]
macro_rules! rebind1 {
    ($x:ty, $y:ty) => { <<$x as Generic1>::Unit as Rebind1<$y>>::Type }
}

#[macro_export]
/// Implementation of Generic1/2 and Rebind1/2 for HKTs with 2 parameter
macro_rules! declare_generic2 {
    ($x:ident) => {
        impl<T0, T1> Generic1 for $x<T0, T1> {
            type Type = T0;
            type Unit = $x<(), T1>;
        }
        impl<T1, Y> Rebind1<Y> for $x<(), T1> {
            type Type = $x<Y, T1>;
        }
        impl<T0, T1> Generic2 for $x<T0, T1> {
            type Type = T1;
            type Unit = $x<T0, ()>;
        }
        impl<T0, Y> Rebind2<Y> for $x<T0, ()> {
            type Type = $x<T0, Y>;
        }
    };
}

#[macro_export]
macro_rules! generic2 {
    ($x:ty) => { <$x as Generic2>::Type }
}


#[macro_export]
macro_rules! rebind2 {
    ($x:ty, $y:ty) => { <<$x as Generic2>::Unit as Rebind1<$y>>::Type }
}

declare_generic1!(Option);
declare_generic2!(Result);
declare_generic1!(Vec);
// TODO: Add more HKTs for std