diff --git a/src/lib.rs b/src/lib.rs index d6baa9b..e2be1f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,4 @@ pub mod model_00; pub mod model_01; #[cfg(any())] pub mod model_02; // ICE +pub mod model_03; diff --git a/src/model_03.rs b/src/model_03.rs new file mode 100644 index 0000000..a054a4e --- /dev/null +++ b/src/model_03.rs @@ -0,0 +1,273 @@ +use std::{ + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; + +use futures::Future; +use pin_project::pin_project; + +pub trait Trait { + type Associated: ?Sized + Trait; + type In<'out: 'tmp, 'tmp, I: 'tmp>; + type Out<'out, A>; +} + +pub trait Impl: Sized { + type Associated: Impl; + fn method<'out: 'tmp, 'tmp>(_: T::In<'out, 'tmp, Self>) -> T::Out<'out, Self::Associated> + where + Self: 'tmp; +} + +pub enum Empty {} + +impl Trait for Empty { + type Associated = Self; + type In<'out: 'tmp, 'tmp, I: 'tmp> = Self; + type Out<'out, A> = Self; +} + +impl Impl for I { + type Associated = I; + fn method<'out: 'tmp, 'tmp>( + empty: ::In<'out, 'tmp, Self>, + ) -> ::Out<'out, Self::Associated> + where + Self: 'tmp, + { + empty + } +} + +pub struct Is(That, T); + +impl Trait for Is { + type Associated = T; + type In<'out: 'tmp, 'tmp, I: 'tmp> = I; + type Out<'out, A> = That; +} + +impl, T: ?Sized + Trait> Impl> for That { + type Associated = Self; + + fn method<'out: 'tmp, 'tmp>( + this: as Trait>::In<'out, 'tmp, Self>, + ) -> as Trait>::Out<'out, Self::Associated> + where + Self: 'tmp, + { + this + } +} + +pub struct To(T); + +impl Trait for To { + type Associated = T; + type In<'out: 'tmp, 'tmp, I: 'tmp> = I; + type Out<'out, A> = A; +} + +impl Output, Output: Impl, T: ?Sized + Trait> Impl> for I { + type Associated = Output; + + fn method<'out: 'tmp, 'tmp>( + this: as Trait>::In<'out, 'tmp, Self>, + ) -> as Trait>::Out<'out, Self::Associated> + where + Self: 'tmp, + { + this() + } +} + +pub trait TraitFn { + type Out: ?Sized + Trait; + fn run(self, _: impl Impl) -> impl Impl; +} + +pub trait Functor { + type W: ?Sized + Trait; + fn pure(_: impl Impl) -> impl Impl>; + fn map>( + _: impl Impl>, + _: F, + ) -> impl Impl>; +} + +pub struct Verbatim; + +impl Functor for Verbatim { + type W = T; + + fn pure(a: impl Impl) -> impl Impl> { + a + } + + fn map>( + a: impl Impl>, + f: F, + ) -> impl Impl> { + f.run(a) + } +} + +pub trait Call { + type Output: Impl; + fn call(self) -> Self::Output; +} + +impl>> Call for I { + type Output = I::Associated; + + fn call(self) -> Self::Output { + I::method(self) + } +} + +pub struct Lazy; + +impl Functor for Lazy { + type W = To; + + fn pure(a: impl Impl) -> impl Impl> { + || a + } + + fn map>( + a: impl Impl>, + f: F, + ) -> impl Impl> { + || f.run(a.call()) + } +} + +pub struct Composition(Uo, Ui); + +impl Functor for Composition { + type W = Uo::W>; + + fn pure(a: impl Impl) -> impl Impl> { + Uo::pure(Ui::pure(a)) + } + + fn map>( + a: impl Impl>, + f: F, + ) -> impl Impl> { + struct Tmp(F, PhantomData<(Uo, Ui, A)>); + impl, Uo: Functor, Ui: Functor> TraitFn> + for Tmp + { + type Out = Ui::W; + + fn run(self, a: impl Impl>) -> impl Impl { + Ui::map(a, self.0) + } + } + Uo::map(a, Tmp::(f, PhantomData)) + } +} + +pub trait Into_ { + fn into_(self) -> A + where + Self: Impl>, + { + Self::method(self) + } +} + +impl Into_ for I {} + +impl B, T: ?Sized + Trait> TraitFn> for F { + type Out = Is; + + fn run(self, a: impl Impl>) -> impl Impl { + self(a.into_()) + } +} + +#[cfg(test)] +fn generic_test() -> impl Impl>> { + let x = U::pure::>(5); + let x = U::map(x, |x| x + 1); + let x = U::map(x, |x| x + 1); + let x = U::map(x, |x| x + 1); + let x = U::map(x, |x| x + 1); + U::map(x, |x| x + 1) +} + +#[test] +fn doubly_lazy() { + type U = Composition; + let x = generic_test::(); + let x = x.call().call().into_::(); + assert_eq!(x, 10); +} + +pub struct FutureTo(T); + +impl Trait for FutureTo { + type Associated = T; + type In<'out: 'tmp, 'tmp, I: 'tmp> = (Pin<&'tmp mut I>, &'tmp mut Context<'out>); + type Out<'out, A> = Poll; +} + +impl, F: Future> Impl> for F { + type Associated = F::Output; + + fn method<'out: 'tmp, 'tmp>( + (this, cx): as Trait>::In<'out, 'tmp, Self>, + ) -> as Trait>::Out<'out, Self::Associated> + where + Self: 'tmp, + { + this.poll(cx) + } +} + +#[pin_project] +struct TraitFuture(#[pin] F, PhantomData); + +impl>> Future for TraitFuture { + type Output = F::Associated; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + F::method((self.project().0, cx)) + } +} + +pub trait IntoFuture_: Impl> { + fn into_future_(self) -> impl Future { + TraitFuture(self, PhantomData) + } +} + +impl>> IntoFuture_ for I {} + +pub struct Futures; + +impl Functor for Futures { + type W = FutureTo; + + fn pure(a: impl Impl) -> impl Impl> { + async { a } + } + + fn map>( + a: impl Impl>, + f: F, + ) -> impl Impl> { + async { f.run(a.into_future_().await) } + } +} + +#[test] +fn with_futures() { + type U = Futures; + let x = generic_test::(); + let x = futures::executor::block_on(x.into_future_()).into_::(); + assert_eq!(x, 10); +}