use core::marker::PhantomData;
use crate::higher::Higher;
pub trait Invariant<B>: Higher {
fn imap<F, G>(self, f: F, g: G) -> Self::Target<B>
where
F: FnMut(Self::Param) -> B,
G: FnMut(B) -> Self::Param;
}
#[macro_export]
macro_rules! invariant_functor {
($name:ident<$( $t:tt ),+>) => {
impl<B, $( $t ),+> $crate::invariant::Invariant<B> for $name<$( $t ),+> {
#[inline]
fn imap<F, G>(self, f: F, _g: G) -> Self::Target<B>
where
F: FnMut(Self::Param) -> B,
G: FnMut(B) -> Self::Param,
{
$crate::functor::Functor::map(self, f)
}
}
};
($name:ident<$( $t:tt ),+>, $ct:tt $(+ $dt:tt )*) => {
impl<B:$ct $(+ $dt )*, $( $t ),+> $crate::invariant::Invariant<B> for $name<$( $t ),+> {
#[inline]
fn imap<F, G>(self, f: F, _g: G) -> Self::Target<B>
where
F: FnMut(Self::Param) -> B,
G: FnMut(B) -> Self::Param,
{
$crate::functor::Functor::map(self, f)
}
}
};
}
#[macro_export]
macro_rules! invariant_contravariant {
($name:ident<$( $t:tt ),+>) => {
impl<B, $( $t ),+> $crate::invariant::Invariant<B> for $name<$( $t ),+> {
#[inline]
fn imap<F, G>(self, _f: F, g: G) -> Self::Target<B>
where
F: FnMut(Self::Param) -> B,
G: FnMut(B) -> Self::Param,
{
$crate::contravariant::Contravariant::contramap(self, g)
}
}
};
($name:ident<$( $t:tt ),+>, $ct:tt $(+ $dt:tt )*) => {
impl<B:$ct $(+ $dt )*, $( $t ),+> $crate::invariant::Invariant<B> for $name<$( $t ),+> {
#[inline]
fn imap<F, G>(self, _f: F, g: G) -> Self::Target<B>
where
F: FnMut(Self::Param) -> B,
G: FnMut(B) -> Self::Param,
{
$crate::contravariant::Contravariant::contramap(self, g)
}
}
};
}
impl<A, B> Invariant<B> for PhantomData<A> {
#[inline]
fn imap<F, G>(self, _f: F, _g: G) -> PhantomData<B>
where
F: FnMut(A) -> B,
G: FnMut(B) -> A,
{
PhantomData
}
}
invariant_functor!(Option<T>);
invariant_functor!(Result<T, E>);
if_std! {
use std::boxed::Box;
use std::collections::*;
use std::hash::Hash;
use std::vec::Vec;
invariant_functor!(Vec<T>);
invariant_functor!(LinkedList<T>);
invariant_functor!(VecDeque<T>);
invariant_functor!(Box<T>);
invariant_functor!(BinaryHeap<T>, Ord);
invariant_functor!(BTreeSet<T>, Ord);
invariant_functor!(HashSet<T>, Hash + Eq);
impl<A, B, K: Hash + Eq> Invariant<B> for HashMap<K, A> {
#[inline]
fn imap<F, G>(self, mut f: F, _g: G) -> HashMap<K, B>
where
F: FnMut(A) -> B,
G: FnMut(B) -> A,
{
self.into_iter().map(|(k, v)| (k, f(v))).collect()
}
}
}