use super::MonadTransformer;
use crate::error::{ComposableError, ComposableResult, IntoErrorContext};
use crate::prelude::HKT;
use crate::traits::monad::Monad;
use std::marker::PhantomData;
use std::sync::Arc;
pub type ReaderCombineFn<Env, M1, A1, B1, C1> =
dyn Fn(&ReaderT<Env, M1, A1>, &ReaderT<Env, M1, B1>) -> ReaderT<Env, M1, C1> + Send + Sync;
pub struct ReaderT<E, M, A> {
run_reader_fn: Arc<dyn Fn(E) -> M + Send + Sync>,
_phantom: PhantomData<A>,
}
impl<E, M, A> Clone for ReaderT<E, M, A>
where
E: 'static,
M: 'static,
{
fn clone(&self) -> Self {
ReaderT {
run_reader_fn: Arc::clone(&self.run_reader_fn),
_phantom: PhantomData,
}
}
}
impl<E, M, A> ReaderT<E, M, A>
where
E: 'static,
M: 'static,
{
#[inline]
pub fn new<F>(f: F) -> Self
where
F: Fn(E) -> M + 'static + Send + Sync,
{
ReaderT {
run_reader_fn: Arc::new(f),
_phantom: PhantomData,
}
}
#[inline]
pub fn run_reader(&self, env: E) -> M
where
E: Clone,
{
(self.run_reader_fn)(env)
}
#[inline]
pub fn ask<P>(pure: P) -> ReaderT<E, M, E>
where
P: Fn(E) -> M + Send + Sync + 'static,
{
ReaderT::new(pure)
}
#[inline]
pub fn local<F>(&self, f: F) -> Self
where
F: Fn(E) -> E + Send + Sync + 'static,
{
let inner_fn = Arc::clone(&self.run_reader_fn);
ReaderT::new(move |e| inner_fn(f(e)))
}
#[inline]
pub fn asks<B, F, P>(f: F, pure: P) -> ReaderT<E, M, B>
where
F: Fn(E) -> B + Send + Sync + 'static,
P: Fn(B) -> M + Send + Sync + 'static,
{
ReaderT::new(move |e| pure(f(e)))
}
#[inline]
pub fn ask_with<B, F, P>(f: F, pure: P) -> ReaderT<E, M, B>
where
F: Fn(&E) -> B + Send + Sync + 'static,
P: Fn(B) -> M + Send + Sync + 'static,
B: 'static,
{
ReaderT::new(move |e| pure(f(&e)))
}
#[inline]
pub fn asks_with<B, C, S, T, P>(select: S, transform: T, pure: P) -> ReaderT<E, M, C>
where
S: Fn(&E) -> B + Send + Sync + 'static,
T: Fn(B) -> C + Send + Sync + 'static,
P: Fn(C) -> M + Send + Sync + 'static,
B: 'static,
C: 'static,
{
ReaderT::new(move |e| pure(transform(select(&e))))
}
}
impl<E, M, A> ReaderT<E, M, A>
where
A: Clone + 'static,
E: Clone + Send + Sync + 'static,
M: Monad + Clone + 'static,
M: HKT<Source = A>,
M::Source: Clone,
{
pub fn fmap_with<B, F, MapFn>(&self, f: F, map_fn: MapFn) -> ReaderT<E, M, B>
where
F: Fn(A) -> B + Clone + Send + Sync + 'static,
MapFn: Fn(M, F) -> M + Send + Sync + 'static,
A: 'static,
B: 'static,
M: 'static,
{
let inner_fn = Arc::clone(&self.run_reader_fn);
let f_clone = f;
ReaderT::new(move |e| {
let m = inner_fn(e);
map_fn(m, f_clone.clone())
})
}
pub fn bind_with<B, N, F, BindFn>(&self, f: F, bind_fn: BindFn) -> ReaderT<E, N, B>
where
F: Fn(A) -> ReaderT<E, N, B> + Clone + Send + Sync + 'static,
BindFn: Fn(M, Arc<dyn Fn(A) -> N + Send + Sync>) -> N + Send + Sync + 'static,
A: 'static,
B: 'static,
M: 'static,
N: 'static,
{
let inner_fn = Arc::clone(&self.run_reader_fn);
let f_clone = f.clone();
ReaderT::new(move |e: E| {
let m = inner_fn(e.clone());
let e_for_closure = e.clone();
let f_for_closure = f_clone.clone();
let bind_closure = Arc::new(move |a: A| {
let reader_b = f_for_closure(a);
reader_b.run_reader(e_for_closure.clone())
});
bind_fn(m, bind_closure)
})
}
pub fn apply_with<B, Func, ApFn>(
&self, f: &ReaderT<E, M, Func>, ap_fn: ApFn,
) -> ReaderT<E, M, B>
where
Func: Fn(A) -> B + Clone + Send + Sync + 'static,
ApFn: Fn(M, M) -> M + Clone + Send + Sync + 'static,
A: 'static,
B: 'static,
M: 'static,
{
let self_fn = Arc::clone(&self.run_reader_fn);
let f_fn = Arc::clone(&f.run_reader_fn);
let ap_fn_clone = ap_fn.clone();
ReaderT::new(move |e: E| {
let ma = self_fn(e.clone());
let mf = f_fn(e);
ap_fn_clone(ma, mf)
})
}
pub fn lift2<B, C, F, CombineFn>(
&self, f: F, combine_fn: CombineFn,
) -> Box<ReaderCombineFn<E, M, A, B, C>>
where
F: Fn(A, B) -> C + Clone + Send + Sync + 'static,
CombineFn: Fn(M, M, F) -> M + Clone + Send + Sync + 'static,
B: Clone + 'static,
C: Clone + 'static,
{
let f_clone = f.clone();
let combine_fn_clone = combine_fn.clone();
Box::new(
move |reader1: &ReaderT<E, M, A>, reader2: &ReaderT<E, M, B>| {
let run1 = Arc::clone(&reader1.run_reader_fn);
let run2 = Arc::clone(&reader2.run_reader_fn);
let f_clone = f_clone.clone();
let combine_fn_inner = combine_fn_clone.clone();
ReaderT::new(move |e: E| {
let ma = run1(e.clone());
let mb = run2(e);
combine_fn_inner(ma, mb, f_clone.clone())
})
},
)
}
pub fn combine_with<B, C, F, CombineFn>(
&self, other: &ReaderT<E, M, B>, f: F, combine_fn: CombineFn,
) -> ReaderT<E, M, C>
where
F: Fn(A, B) -> C + Clone + Send + Sync + 'static,
CombineFn: for<'a> Fn(M, M, Box<dyn Fn(&A, &B) -> C + Send + Sync + 'a>) -> M
+ Clone
+ Send
+ Sync
+ 'static,
A: Clone + 'static,
B: Clone + 'static,
C: Clone + 'static,
M: HKT<Output<C> = M>,
{
let self_fn = Arc::clone(&self.run_reader_fn);
let other_fn = Arc::clone(&other.run_reader_fn);
let f = Arc::new(f);
let combine_fn = Arc::new(combine_fn);
ReaderT::new(move |e: E| {
let ma = self_fn(e.clone());
let mb = other_fn(e.clone());
let f_clone = f.clone();
let boxed_f = Box::new(move |a: &A, b: &B| {
let a_owned = a.clone();
let b_owned = b.clone();
f_clone(a_owned, b_owned)
}) as Box<dyn Fn(&A, &B) -> C + Send + Sync + 'static>;
combine_fn.clone()(ma, mb, boxed_f)
})
}
#[inline]
pub fn unwrap_with(self, env: E) -> M {
self.run_reader(env)
}
}
impl<E, M, A> ReaderT<E, M, A>
where
E: Clone + 'static,
M: Monad + Clone + 'static,
A: Clone + 'static,
M: HKT<Source = A, Output<A> = M>,
M::Source: Clone,
{
#[inline]
pub fn fmap<B, F>(&self, f: F) -> ReaderT<E, M, B>
where
F: Fn(A) -> B + Clone + Send + Sync + 'static,
B: Clone + 'static,
M: HKT<Output<B> = M>,
{
let reader_fn = Arc::clone(&self.run_reader_fn);
ReaderT::new(move |e: E| {
let ma = reader_fn(e);
let f_clone = f.clone();
M::fmap(&ma, move |a: &A| f_clone(a.clone()))
})
}
#[inline]
pub fn bind<B, F>(&self, f: F) -> ReaderT<E, M, B>
where
F: Fn(A) -> ReaderT<E, M, B> + Clone + Send + Sync + 'static,
B: Clone + 'static,
M: HKT<Output<B> = M>,
{
let inner_fn = self.run_reader_fn.clone();
let f = Arc::new(f);
ReaderT::new(move |e: E| {
let ma = inner_fn(e.clone());
let f_clone = f.clone();
let e_clone = e.clone();
M::bind::<B, _>(&ma, move |source_ref: &M::Source| {
let a_owned = source_ref.clone();
let a_value: A = unsafe { std::mem::transmute_copy(&a_owned) };
f_clone(a_value).run_reader(e_clone.clone())
})
})
}
pub fn apply<B, Func>(&self, f: &ReaderT<E, M, Func>) -> ReaderT<E, M, B>
where
Func: Fn(A) -> B + Clone + Send + Sync + 'static,
B: Clone + 'static,
M: HKT<Output<B> = M> + HKT<Output<Func> = M>,
{
self.combine(f, |a, func| func(a))
}
#[inline]
pub fn combine<B, C, F>(&self, other: &ReaderT<E, M, B>, f: F) -> ReaderT<E, M, C>
where
F: Fn(A, B) -> C + Clone + Send + Sync + 'static,
B: Clone + 'static,
C: Clone + 'static,
M: HKT<Output<B> = M> + HKT<Output<C> = M>,
{
let self_fn = self.run_reader_fn.clone();
let other_fn = other.run_reader_fn.clone();
let f = Arc::new(f);
ReaderT::new(move |e: E| {
let ma = self_fn(e.clone());
let mb = other_fn(e.clone());
let f_clone = f.clone();
M::lift2(move |a: &A, b: &B| f_clone(a.clone(), b.clone()), &ma, &mb)
})
}
}
impl<E, Err, A> ReaderT<E, Result<A, Err>, A>
where
E: Clone + 'static,
Err: 'static,
A: Clone + 'static,
{
pub fn try_run_reader(&self, env: E) -> ComposableResult<A, Err> {
self.run_reader(env).map_err(ComposableError::new)
}
pub fn try_run_reader_with_context<C>(&self, env: E, context: C) -> ComposableResult<A, Err>
where
C: IntoErrorContext,
{
let context = context.into_error_context();
self.run_reader(env)
.map_err(|e| ComposableError::new(e).with_context(context.clone()))
}
pub fn map_error<F, Err2>(&self, f: F) -> ReaderT<E, Result<A, Err2>, A>
where
F: Fn(Err) -> Err2 + Send + Sync + 'static,
Err2: 'static,
{
let run_reader_fn_clone = self.run_reader_fn.clone();
ReaderT::new(move |e: E| run_reader_fn_clone(e).map_err(&f))
}
}
impl<E, M, A> MonadTransformer for ReaderT<E, M, A>
where
E: Clone + 'static,
M: Monad + Send + Sync + Clone + 'static,
A: Clone + 'static,
{
type BaseMonad = M;
#[inline]
fn lift(base: Self::BaseMonad) -> Self {
let base_clone = base.clone();
ReaderT::new(move |_| base_clone.clone())
}
}
impl<E, M, A> ReaderT<E, M, A>
where
E: 'static,
M: 'static,
A: 'static,
{
#[inline]
pub fn pure<F>(value: A, pure_fn: F) -> Self
where
E: Clone + Send + Sync + 'static,
A: Clone + Send + Sync + 'static,
F: Fn(A) -> M + Clone + Send + Sync + 'static,
M: Clone + Send + Sync + 'static,
{
ReaderT::new(move |_: E| pure_fn(value.clone()))
}
}