lubeck 0.0.0-prealpha.5-abandoned

Functional programming framework written in cutting edge rust
Documentation
// Ahhhhhh nevermind, that was stupid. This here doesn't work. I won't delete it though to try to
// not make the same attempt again.
//
// While the code below type-checks (which made me very happy for a second), it's unusable. I tried
// to construct an example with it as follows
//
// ```
//  type ReaderOptT<'a, R, A> = OptionT<'a, Reader<'a, R, A>, A>;
//
//  let x = ReaderOptT::<'_, i32, i32> {
//      run_option_t: Reader::new(|v| 0),
//      _pd: std::marker::PhantomData::default(),
//  };
// ```
//
// but of course this doesn't work, since
//
// - `M = Reader<i32, i32>`
// - trait bound requires `M: GenType<Type<Option<i32>> = M>`
// - substituting leads to `Reader<i32, i32>: GenType<Type<Option<i32>> = Reader<i32, i32>>`
// - this isn't the case since
// ```
// Reader<i32, i32>::Type<Option<i32>>
//  == Reader<i32, Option<i32>>
//  != Reader<i32, i32>
// ```
//
// Dang :/
//
// Update: With a little fix it compiles! But it doesn't quiet work as I was expecting. Here is an
// explanation: I had to go from
//
// `type ReaderOptT<'a, R, A> = OptionT<'a, Reader<'a, R, A>, A>;`
//
// to
//
// `type ReaderOptT<'a, R, A> = OptionT<'a, Reader<'a, R, Option<A>>, A>;`
//
// After fiddling around with the now compiling code, I realized that the `GenType` is kind of
// wrong. It is of type `M<..>` when it should be `OptionT<..>`. This means, if we call `fmap`,
// then the one for `M` is called and we still need to consider that there is an option in our
// passed function, i.e. instead of
//
// `let _ = x.fmap(|x| x + 1);`
//
// we have
//
// `let _ = x.fmap(|opt_x| opt_x.map(|x| x + 1));`
//
// Not cool, but still better than the code completely not working at all.

use crate::prelude::{Applicative, Functor, GenType, Monad};
use std::marker::PhantomData;

pub struct OptionT<'a, M, A>
where
    M: GenType<Type<Option<A>> = M> + Monad<'a, Option<A>>,
    A: 'a,
{
    pub run_option_t: M::Type<Option<A>>,
    pub _pd: PhantomData<&'a ()>,
}

impl<'a, M, A> GenType for OptionT<'a, M, A>
where
    M: GenType<Type<Option<A>> = M> + Monad<'a, Option<A>>,
    A: 'a,
{
    type Type<T> = M::Type<Option<T>>;
}

impl<'a, M, A> Functor<'a, A> for OptionT<'a, M, A>
where
    M: GenType<Type<Option<A>> = M> + Monad<'a, Option<A>>,
    A: 'a,
{
    fn fmap<F, B: 'a>(self, f: F) -> Self::Type<B>
    where
        F: Fn(A) -> B + 'a,
    {
        self.run_option_t
            .fmap(move |opt_a: Option<A>| opt_a.fmap(|a| f(a)))
    }
}

impl<'a, M, A> Applicative<'a, A> for OptionT<'a, M, A>
where
    M: GenType<Type<Option<A>> = M>
        + Applicative<'a, Option<A>, PureT<Option<A>> = Option<A>>
        + Monad<'a, Option<A>>,
    A: 'a,
{
    type PureT<T> = T where T: 'a ;

    fn pure(a: Self::PureT<A>) -> Self::Type<Self::PureT<A>> {
        M::pure(Some(a))
    }

    fn app<F, B>(self, f: Self::Type<F>) -> Self::Type<B>
    where
        F: Fn(A) -> B + 'a,
        B: 'a,
    {
        todo!(
            "Probably something like 

              // self = M<Option<A>>
              self.app(

                  // f before fmap = M<Option<Fn(A) -> B>>
                  // f after fmap  = M<Fn(Option<A>) -> Option<B>> => can use app of M
                  f
                  .fmap(
                      // f_in_opt = Option<Fn(A) -> B>
                      |f_in_opt| 

                      // Fn(Option<A>) -> Option<B> 
                      move |opt_a: Option<A>| opt_a.app(f_in_opt))
                  )

              "
        )
    }
}