1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Invariant functors.

use core::marker::PhantomData;

use crate::higher::Higher;

/// Invariant functor (also known as exponential functor).
pub trait Invariant<B>: Higher {
    /// Transform a `Self<A>` into a `Self<B>` by providing a transformation from `A` to `B`
    /// and one from `B` to `A`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rust2fun::prelude::*;
    ///
    /// let x = Some("1".to_string());
    /// let actual = x.imap(|s| s.parse::<i32>().unwrap(), |i| i.to_string());
    /// assert_eq!(Some(1), actual);
    /// ```
    fn imap<F, G>(self, f: F, g: G) -> Self::Target<B>
    where
        F: FnMut(Self::Param) -> B,
        G: FnMut(B) -> Self::Param;
}

// TODO. Refactor this when specialization is stable.
/// Macro to implement [Invariant] for types implementing [Functor].
///
/// [Functor]: crate::functor::Functor
#[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)
            }
        }
    };
}

// TODO. Refactor this when specialization is stable.
/// Macro to implement [Invariant] for types implementing a contravariant [Functor].
///
/// [Functor]: crate::functor::Functor
#[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()
        }
    }
}