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
// Lifetime bound to be used in `for<'lt>` blocks.
pub type Bound<'lt, 'bound> = &'lt &'bound ();

#[doc(hidden)]
#[macro_export]
macro_rules! type_class {
    (for<$lt:lifetime, $ctx:lifetime> $vis:vis $name:ident $(: $($bound:tt)*)?) => {
        $vis mod $name {
            use super::*;

            pub trait ForLt<$lt, $ctx: $lt, B> {
                type T: $($($bound)*)?;
            }

            pub trait Hkt<$ctx>: for<$lt> ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx>> {}

            impl<$ctx, T> Hkt<$ctx> for T 
            where 
                T: for<$lt> ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx>> 
            {}

            pub type T<$lt, $ctx, H> = <H as ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx>>>::T;
        }
    };
}

#[doc(inline)]
pub use type_class;

#[doc(hidden)]
#[macro_export]
macro_rules! hkt {
    (
        ($($type_class:tt)*):
        for<$lt:lifetime, $ctx:lifetime> 
        $vis:vis $name:ident$([$($generic:tt)*])?
        $(where {$($bound:tt)*})? => $($type:tt)*
    ) => {
        $vis struct $name<$ctx $(, $($generic)*)?>(core::marker::PhantomData<fn() -> (&$ctx (), $($($generic)*)?)>);

        impl<$lt, $ctx $(, $($generic)*)?> $($type_class)*::ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx>> for $name<$ctx $(, $($generic)*)?> 
        $(where $($bound)*)? 
        {
            type T = $($type)*;
        }
    }
}

#[doc(inline)]
pub use hkt;

#[cfg(test)]
mod test {
    use super::*;

    trait Demo<'lt, 'ctx> {
        fn add(&self, x: &'lt &'ctx i32) -> i32;
    }

    impl<'lt, 'ctx> Demo<'lt, 'ctx> for i32 {
        fn add(&self, x: &'lt &'ctx i32) -> i32 {
            self + **x
        }
    }

    impl<'lt, 'ctx> Demo<'lt, 'ctx> for &'lt (dyn Demo<'lt, 'ctx> + 'lt) {
        fn add(&self, x: &'lt &'ctx i32) -> i32 {
            (**self).add(x)
        }
    }

    type_class!(for<'lt, 'ctx> any_t: Demo<'lt, 'ctx>);

    hkt!((any_t): for<'lt, 'ctx> X => &'lt (dyn Demo<'lt, 'ctx> + 'lt));

    fn demo<'lt, 'ctx, T: any_t::Hkt<'ctx>>(x: any_t::T<'lt, 'ctx, T>, y: &'lt &'ctx i32) {
        assert_eq!(x.add(y), 10);
    }

    #[test]
    fn can_use_hkt() {
        let ctx = 2;
        let ctx = &ctx;

        {
            let local = &ctx;
            let z: &dyn Demo<'_, '_> = &8i32;
            demo::<X<'_>>(z, local);
        }
    }
}