//! 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! 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 $(, $($generic)*)?>
where $($($bound)*)? $($($for_bound)*)?
{
type T$(: $($provides)+)?;
}
pub trait Trait<$ctx $(, $($generic)*)?>: for<$lt> ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx $(, $($generic)*)?> $(, $($generic)*)?>
where
$($($bound)*)?
{}
impl<$ctx, ____ $(, $($generic)*)?> Trait<$ctx $(, $($generic)*)?> for ____
where
____: for<$lt> ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx $(, $($generic)*)?> $(, $($generic)*)?>,
$($($bound)*)?
{}
pub type T<$lt, $ctx, H $(, $($generic)*)?> = <H as ForLt<$lt, $ctx, $crate::hkt::Bound<$lt, $ctx $(, $($generic)*)?> $(, $($generic)*)?>>::T;
}
};
}
#[doc(inline)]
pub use higher_ranked_trait;
/// Generate a higher-ranked type.
#[doc(hidden)]
#[macro_export]
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 $(, $($generic)*)?> $(, $($type_class_generic)*)?> for $name<$ctx $(, $($generic)*)?>
where $($($bound)*)? $($($higher_bound)*)?
{
type T = $for_lt_type;
}
}
}
#[doc(inline)]
pub use higher_ranked_type;
higher_ranked_trait! {
pub type class AnySend['ctx]: [for<'lt> Send]
}
#[cfg(test)]
mod test {
use super::*;
/// 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, T: Send + 'lt> Demo<'lt, 'ctx, T> for i32 {
fn add(&self, x: &'lt &'ctx i32) -> i32 {
self + **x
}
}
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)
}
}
// 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
}
}
// 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)
}
// 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);
}
#[test]
fn can_use_hkt() {
let ctx = 2;
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<'_, '_, i32> = &8i32;
demo::<X<'_, i32>>(z, local);
}
}
}