Diffstat (limited to 'src/hkt.rs')
| -rw-r--r-- | src/hkt.rs | 134 |
1 files changed, 103 insertions, 31 deletions
@@ -1,79 +1,150 @@ -// Lifetime bound to be used in `for<'lt>` blocks. -pub type Bound<'lt, 'bound> = &'lt &'bound (); - +//! Higher-ranked traits and types. +//! +//! A higher-ranked type is a type that needs a lifetime to be "complete". Consider the following +//! code. +//! ```rs +//! type IntBorrow<'a> = &'a i32; +//! ``` +//! What if we could pass just `IntBorrow` around as a type; without it's lifetime. +//! In Rust syntax, this type would be `for<'a> IntBorrow<'a>`. `'a` can be filled in with any +//! lifetime. Sadly, Rust only allows `for<'a>` in trait objects. However, trait object provide +//! enough for us to implement arbitrary hifher-ranked types with some extra effort. +//! +//! In this module the [`higher_ranked_type!`] macro generates a type that needs a lifetime to +//! be complete. Before a higher-ranked type can be generated we need a higher-ranked trait to +//! describe what behavior the resulting "true" type will have. +//! +//! The [`higher_ranked_trait!`] macro cgenerates a trait that can be used with +//! [`higher_ranked_type!`] and as a bound by code wanting to accept a higher ranked type. +//! Bounding a generic by a higher-ranked trait simultaniously allows any higher-ranked type with +//! a "true" type with the required traits and any lifetime to be given to that "true" type. + +/// Used to cause `'lt` to have extra bounds when using `for<'lt>` syntax. +/// +/// Including this type where a `for<'lt>` is being generated will result in the following bounds +/// being applied. +/// * `'bound: 'lt` - `'lt` will **not** outlive `'bound`. +/// * `T: 'lt` - `T` will outlive `'lt`. +pub type Bound<'lt, 'bound, T = ()> = (&'lt &'bound (), &'lt T); + +/// Generate a higher-ranked trait. #[doc(hidden)] #[macro_export] -macro_rules! type_class { - (for<$lt:lifetime, $ctx:lifetime> $vis:vis $name:ident $(: $($bound:tt)*)?) => { +macro_rules! higher_ranked_trait { + { + $vis:vis type class $name:ident[$ctx:lifetime $(, $($generic:tt)*)?]: [for<$lt:lifetime> $($($provides:tt)+)?] + $(where { + $($bound:tt)* + })? + $(for where { + $($for_bound:tt)* + })? + } => { $vis mod $name { + #![allow(unused, non_snake_case)] + use super::*; - pub trait ForLt<$lt, $ctx: $lt, B> { - type T: $($($bound)*)?; + pub trait ForLt<$lt, $ctx: $lt, B $(, $($generic)*)?> + where $($($bound)*)? $($($for_bound)*)? + + { + type T$(: $($provides)+)?; } - pub trait Hkt<$ctx>: for<$lt> ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx>> {} + pub trait Trait<$ctx $(, $($generic)*)?>: for<$lt> ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx $(, $($generic)*)?> $(, $($generic)*)?> + where + $($($bound)*)? + {} - impl<$ctx, T> Hkt<$ctx> for T + impl<$ctx, ____ $(, $($generic)*)?> Trait<$ctx $(, $($generic)*)?> for ____ where - T: for<$lt> ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx>> + ____: for<$lt> ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx $(, $($generic)*)?> $(, $($generic)*)?>, + $($($bound)*)? {} - pub type T<$lt, $ctx, H> = <H as ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx>>>::T; + pub type T<$lt, $ctx, H $(, $($generic)*)?> = <H as ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx $(, $($generic)*)?> $(, $($generic)*)?>>::T; } }; } #[doc(inline)] -pub use type_class; +pub use higher_ranked_trait; +/// Generate a higher-ranked type. #[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)* - ) => { +macro_rules! higher_ranked_type { + { + $vis:vis type $name:ident[$ctx:lifetime $(, $($generic:tt)*)?]: ($($type_class:tt)*)$([$($type_class_generic:tt)*])? + $(where {$($bound:tt)*})? + = for<$lt:lifetime> $for_lt_type:ty + $(where {$($higher_bound: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)*)? + impl<$lt, $ctx $(, $($generic)*)?> $($type_class)*::ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx $(, $($generic)*)?> $(, $($type_class_generic)*)?> for $name<$ctx $(, $($generic)*)?> + where $($($bound)*)? $($($higher_bound)*)? { - type T = $($type)*; + type T = $for_lt_type; } } } #[doc(inline)] -pub use hkt; +pub use higher_ranked_type; + +higher_ranked_trait! { + pub type class AnySend['ctx]: [for<'lt> Send] +} #[cfg(test)] mod test { use super::*; - trait Demo<'lt, 'ctx> { + /// Some trait with two lifetimes and a generic. + trait Demo<'lt, 'ctx, T: Send + 'lt> { fn add(&self, x: &'lt &'ctx i32) -> i32; } - impl<'lt, 'ctx> Demo<'lt, 'ctx> for i32 { + impl<'lt, 'ctx, T: Send + 'lt> Demo<'lt, 'ctx, T> 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) { + impl<'lt, 'ctx, T: Send + 'lt> Demo<'lt, 'ctx, T> for &'lt (dyn Demo<'lt, 'ctx, T> + 'lt) { fn add(&self, x: &'lt &'ctx i32) -> i32 { (**self).add(x) } } - type_class!(for<'lt, 'ctx> any_t: Demo<'lt, 'ctx>); + // Higher-ranked trait that requires the "true" type to implement Demo. + higher_ranked_trait! { + type class Any['ctx, T]: [for<'lt> Demo<'lt, 'ctx, T>] + where { + T: Send, + } + for where { + T: 'lt + } + } - hkt!((any_t): for<'lt, 'ctx> X => &'lt (dyn Demo<'lt, 'ctx> + 'lt)); + // Higher-ranked type with a "true" type of a borrow of a trait object. + // The complex part to support here is the `+ 'lt`. + // This entire module is specialized to support this usecase because that is what treaty needs + // for it's API. + higher_ranked_type! { + type X['ctx, T]: (Any)[T] + where { + T: Send, + } = for<'lt> &'lt (dyn Demo<'lt, 'ctx, T> + 'lt) + } - fn demo<'lt, 'ctx, T: any_t::Hkt<'ctx>>(x: any_t::T<'lt, 'ctx, T>, y: &'lt &'ctx i32) { + // We bound the generic T by the higher-ranked trait Any. + // + // We then inject a 'lt into the higher-ranked type to get a "true" type. + fn demo<'lt, 'ctx, T: Any::Trait<'ctx, i32>>(x: Any::T<'lt, 'ctx, T, i32>, y: &'lt &'ctx i32) { assert_eq!(x.add(y), 10); } @@ -83,9 +154,10 @@ mod test { let ctx = &ctx; { + // The lifetimes here can't be 'static because local isn't static even though z could be. let local = &ctx; - let z: &dyn Demo<'_, '_> = &8i32; - demo::<X<'_>>(z, local); + let z: &dyn Demo<'_, '_, i32> = &8i32; + demo::<X<'_, i32>>(z, local); } } } |