Diffstat (limited to 'src/hkt.rs')
| -rw-r--r-- | src/hkt.rs | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/src/hkt.rs b/src/hkt.rs new file mode 100644 index 0000000..d41a7ae --- /dev/null +++ b/src/hkt.rs @@ -0,0 +1,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); + } + } +} |