From 590cc0d6b2e8e1c0a9341fdeab7df05cb8f41c8d Mon Sep 17 00:00:00 2001
From: timofey <tim@ongoteam.yaconnect.com>
Date: Mon, 31 Jul 2023 17:15:35 +0000
Subject: [PATCH] `Stackless` covariant

---
 src/func/instances/stackless.rs | 110 +++++++++++++++++++++-----------
 1 file changed, 72 insertions(+), 38 deletions(-)

diff --git a/src/func/instances/stackless.rs b/src/func/instances/stackless.rs
index 1fc409e..0badc89 100644
--- a/src/func/instances/stackless.rs
+++ b/src/func/instances/stackless.rs
@@ -11,7 +11,7 @@ use std::{cell::Cell, rc::Rc};
 use crate::func::class_prelude::*;
 use crate::func::derivations::{ApplicativeLA2ViaSeq, ApplicativeTupleViaLA2};
 
-struct Wrapper<'a, F>(F, PhantomData<&'a ()>);
+struct Wrapper<Q, F>(F, PhantomData<Q>);
 
 trait Atom {
     fn next<'t>(self: Box<Self>) -> Oet<'t>
@@ -31,10 +31,7 @@ fn satom<'a, F: 'a + FnOnce() -> Oet<'a>>(f: F) -> Oet<'a> {
     Some(atom(f))
 }
 
-impl<'a, F> Atom for Wrapper<'a, F>
-where
-    F: 'a + FnOnce() -> Oet<'a>,
-{
+impl<'a, F: 'a + FnOnce() -> Oet<'a>> Atom for Wrapper<&'a (), F> {
     fn next<'t>(self: Box<Self>) -> Oet<'t>
     where
         Self: 't,
@@ -67,7 +64,72 @@ impl<'a> EvalTree<'a> {
     }
 }
 
-type StackessDyn<'a, A> = dyn 'a + FnOnce(Box<dyn 'a + FnOnce(A)>) -> Oet<'a>;
+trait IntoOet<A> {
+    fn into_oet<'t>(self: Box<Self>, f: Box<dyn 't + FnOnce(A)>) -> Oet<'t>
+    where
+        Self: 't,
+        A: 't;
+}
+
+impl<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> Stackless<'a, B>> IntoOet<B>
+    for Wrapper<(&'a (), B), (Stackless<'a, A>, F)>
+{
+    fn into_oet<'t>(self: Box<Self>, f: Box<dyn 't + FnOnce(B)>) -> Oet<'t>
+    where
+        Self: 't,
+        A: 't,
+    {
+        let cell_l = Rc::new(Cell::new(None));
+        let cell_r = cell_l.clone();
+        let (sstackless, sf) = self.0;
+        Some(EvalTree::Composite(
+            batom(move || sstackless.call(move |a| set_cell(cell_l, a))),
+            batom(move || {
+                let stackless = sf(get_cell(cell_r));
+                satom(|| stackless.0.into_oet(f))
+            }),
+        ))
+    }
+}
+
+impl<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B> IntoOet<B>
+    for Wrapper<(&'a (), B, ()), (Stackless<'a, A>, F)>
+{
+    fn into_oet<'t>(self: Box<Self>, f: Box<dyn 't + FnOnce(B)>) -> Oet<'t>
+    where
+        Self: 't,
+        A: 't,
+    {
+        let cell_l = Rc::new(Cell::new(None));
+        let cell_r = cell_l.clone();
+        let (sstackless, sf) = self.0;
+        Some(EvalTree::Composite(
+            batom(move || sstackless.call(move |a| set_cell(cell_l, a))),
+            batom(move || {
+                let b = sf(get_cell(cell_r));
+                satom(|| {
+                    f(b);
+                    None
+                })
+            }),
+        ))
+    }
+}
+
+impl<A> IntoOet<A> for Wrapper<(), A> {
+    fn into_oet<'t>(self: Box<Self>, f: Box<dyn 't + FnOnce(A)>) -> Oet<'t>
+    where
+        Self: 't,
+        A: 't,
+    {
+        satom(|| {
+            f(self.0);
+            None
+        })
+    }
+}
+
+type StackessDyn<'a, A> = dyn 'a + IntoOet<A>;
 
 pub struct Stackless<'a, A: 'a>(Box<StackessDyn<'a, A>>);
 
@@ -86,41 +148,18 @@ fn get_cell<A>(cell: Rc<Cell<Option<A>>>) -> A {
 
 impl<'a, A: 'a> Stackless<'a, A> {
     fn call(self, f: impl 'a + FnOnce(A)) -> Oet<'a> {
-        self.0(Box::new(f))
+        self.0.into_oet(Box::new(f))
     }
 
     /// Method-like equivalent of [`Monad::bind`],
     /// the preferred way to chain [`Stackless<A>`] and `FnOnce(A) -> Stackless<B>` into [`Stackless<B>`].
     pub fn bind<B: 'a>(self, f: impl 'a + FnOnce(A) -> Stackless<'a, B>) -> Stackless<'a, B> {
-        Stackless(Box::new(|takesb| {
-            let cell_l = Rc::new(Cell::new(None));
-            let cell_r = cell_l.clone();
-            Some(EvalTree::Composite(
-                batom(move || self.call(move |a| set_cell(cell_l, a))),
-                batom(move || {
-                    let stackless = f(get_cell(cell_r));
-                    satom(|| stackless.0(takesb))
-                }),
-            ))
-        }))
+        Stackless(Box::new(Wrapper((self, f), PhantomData)))
     }
 
     /// Method-like equivalent of [`Functor::fmap`].
     pub fn map<B: 'a>(self, f: impl 'a + FnOnce(A) -> B) -> Stackless<'a, B> {
-        Stackless(Box::new(|takesb| {
-            let cell_l = Rc::new(Cell::new(None));
-            let cell_r = cell_l.clone();
-            Some(EvalTree::Composite(
-                batom(move || self.call(move |a| set_cell(cell_l, a))),
-                batom(move || {
-                    let b = f(get_cell(cell_r));
-                    satom(|| {
-                        takesb(b);
-                        None
-                    })
-                }),
-            ))
-        }))
+        Stackless(Box::new(Wrapper((self, f), PhantomData)))
     }
 
     /// Evaluate. Process is loop-like on the inside
@@ -138,12 +177,7 @@ impl<'a, A: 'a> Stackless<'a, A> {
 
 impl<'a, A: 'a> From<A> for Stackless<'a, A> {
     fn from(value: A) -> Self {
-        Stackless(Box::new(|takesa| {
-            satom(|| {
-                takesa(value);
-                None
-            })
-        }))
+        Stackless(Box::new(Wrapper(value, PhantomData)))
     }
 }