Diffstat (limited to 'src/hkt.rs')
-rw-r--r--src/hkt.rs91
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);
+ }
+ }
+}