Diffstat (limited to 'src/hkt.rs')
-rw-r--r--src/hkt.rs134
1 files changed, 103 insertions, 31 deletions
diff --git a/src/hkt.rs b/src/hkt.rs
index 8a7a48c..466d358 100644
--- a/src/hkt.rs
+++ b/src/hkt.rs
@@ -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);
}
}
}