#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use crate::typeclasses::{Applicatio, Functor, Monad};
#[cfg(feature = "alloc")]
pub struct ReaderT<E, M> {
run_fn: Box<dyn Fn(&E) -> M + Send + Sync>,
}
#[cfg(feature = "alloc")]
impl<E, M> ReaderT<E, M> {
#[inline]
pub fn new<F>(f: F) -> Self
where
F: Fn(&E) -> M + Send + Sync + 'static,
{
ReaderT {
run_fn: Box::new(f),
}
}
#[inline]
pub fn run(&self, env: &E) -> M {
(self.run_fn)(env)
}
#[inline]
pub fn ask() -> Self
where
E: Clone + 'static,
M: From<E> + 'static,
{
ReaderT::new(|env: &E| M::from(env.clone()))
}
#[inline]
pub fn local<F>(self, f: F) -> Self
where
F: Fn(&E) -> E + Send + Sync + 'static,
E: 'static,
M: 'static,
{
ReaderT::new(move |env: &E| {
let modified_env = f(env);
(self.run_fn)(&modified_env)
})
}
}
#[cfg(feature = "alloc")]
impl<E: 'static, M: Functor + 'static> ReaderT<E, M> {
#[inline]
pub fn map<B, F>(self, f: F) -> ReaderT<E, M::Target<B>>
where
F: Fn(M::Inner) -> B + Send + Sync + 'static,
M::Target<B>: 'static,
{
ReaderT::new(move |env: &E| {
let m = (self.run_fn)(env);
Functor::map(m, &f)
})
}
}
#[cfg(feature = "alloc")]
impl<E: 'static, M: Monad + 'static> ReaderT<E, M> {
#[inline]
pub fn flat_map<B, F>(self, f: F) -> ReaderT<E, M::Target<B>>
where
F: Fn(M::Inner) -> ReaderT<E, M::Target<B>> + Send + Sync + 'static,
M::Target<B>: 'static,
{
ReaderT::new(move |env: &E| {
let m = (self.run_fn)(env);
m.flat_map(|a| f(a).run(env))
})
}
}
#[cfg(feature = "alloc")]
impl<E: 'static, M: Applicatio + 'static> ReaderT<E, M>
where
M::Inner: Clone + Send + Sync,
{
#[inline]
pub fn pure(value: M::Inner) -> Self {
ReaderT::new(move |_: &E| M::pure(value.clone()))
}
}
#[cfg(feature = "alloc")]
impl<E: 'static, A: 'static> ReaderT<E, Option<A>> {
#[inline]
pub fn none() -> Self {
ReaderT::new(|_: &E| None)
}
#[inline]
pub fn apply<B, F>(self, f: ReaderT<E, Option<F>>) -> ReaderT<E, Option<B>>
where
F: FnOnce(A) -> B + Clone + Send + Sync + 'static,
B: 'static,
{
ReaderT::new(move |env: &E| match ((self.run_fn)(env), f.run(env)) {
(Some(a), Some(func)) => Some(func(a)),
_ => None,
})
}
#[inline]
pub fn map2<B, C, F>(self, other: ReaderT<E, Option<B>>, f: F) -> ReaderT<E, Option<C>>
where
F: Fn(A, B) -> C + Send + Sync + 'static,
B: 'static,
C: 'static,
{
ReaderT::new(move |env: &E| match ((self.run_fn)(env), other.run(env)) {
(Some(a), Some(b)) => Some(f(a, b)),
_ => None,
})
}
}
#[cfg(feature = "alloc")]
impl<E: 'static, A: 'static, Err: 'static> ReaderT<E, Result<A, Err>> {
#[inline]
pub fn ok(value: A) -> Self
where
A: Clone + Send + Sync,
{
ReaderT::new(move |_: &E| Ok(value.clone()))
}
#[inline]
pub fn err(error: Err) -> Self
where
Err: Clone + Send + Sync,
{
ReaderT::new(move |_: &E| Err(error.clone()))
}
#[inline]
pub fn map_err<Err2, F>(self, f: F) -> ReaderT<E, Result<A, Err2>>
where
F: Fn(Err) -> Err2 + Send + Sync + 'static,
Err2: 'static,
{
ReaderT::new(move |env: &E| (self.run_fn)(env).map_err(&f))
}
#[inline]
pub fn map2_ok<B, C, F>(
self,
other: ReaderT<E, Result<B, Err>>,
f: F,
) -> ReaderT<E, Result<C, Err>>
where
F: Fn(A, B) -> C + Send + Sync + 'static,
B: 'static,
C: 'static,
{
ReaderT::new(move |env: &E| match ((self.run_fn)(env), other.run(env)) {
(Ok(a), Ok(b)) => Ok(f(a, b)),
(Err(e), _) | (_, Err(e)) => Err(e),
})
}
}
#[cfg(feature = "alloc")]
impl<E: 'static, A: 'static> ReaderT<E, Vec<A>> {
#[inline]
pub fn singleton(value: A) -> Self
where
A: Clone + Send + Sync,
{
ReaderT::new(move |_: &E| alloc::vec![value.clone()])
}
#[inline]
pub fn empty() -> Self {
ReaderT::new(|_: &E| alloc::vec![])
}
#[inline]
pub fn flat_map_vec<B, F>(self, f: F) -> ReaderT<E, Vec<B>>
where
F: Fn(A) -> ReaderT<E, Vec<B>> + Send + Sync + 'static,
B: 'static,
{
ReaderT::new(move |env: &E| {
(self.run_fn)(env)
.into_iter()
.flat_map(|a| f(a).run(env))
.collect()
})
}
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
#[test]
fn test_reader_t_new_and_run() {
let reader: ReaderT<i32, Option<i32>> = ReaderT::new(|env: &i32| Some(*env * 2));
assert_eq!(reader.run(&21), Some(42));
}
#[test]
fn test_reader_t_pure() {
let reader: ReaderT<(), Option<i32>> = ReaderT::pure(42);
assert_eq!(reader.run(&()), Some(42));
}
#[test]
fn test_reader_t_none() {
let reader: ReaderT<(), Option<i32>> = ReaderT::none();
assert_eq!(reader.run(&()), None);
}
#[test]
fn test_reader_t_map() {
let reader: ReaderT<(), Option<i32>> = ReaderT::pure(21);
let doubled = reader.map(|x| x * 2);
assert_eq!(doubled.run(&()), Some(42));
}
#[test]
fn test_reader_t_flat_map() {
let reader: ReaderT<i32, Option<i32>> = ReaderT::new(|env: &i32| Some(*env));
let chained = reader.flat_map(|val| {
ReaderT::new(move |env: &i32| if *env > 0 { Some(val * 2) } else { None })
});
assert_eq!(chained.run(&5), Some(10));
assert_eq!(chained.run(&0), None);
}
#[test]
#[allow(clippy::type_complexity)] fn test_reader_t_apply() {
let val: ReaderT<(), Option<i32>> = ReaderT::pure(21);
let func: ReaderT<(), Option<fn(i32) -> i32>> =
ReaderT::pure((|x: i32| x * 2) as fn(i32) -> i32);
let result = val.apply(func);
assert_eq!(result.run(&()), Some(42));
}
#[test]
fn test_reader_t_local() {
let reader: ReaderT<i32, Option<i32>> = ReaderT::new(|n: &i32| Some(*n * 2));
let modified = reader.local(|n: &i32| *n + 10);
assert_eq!(modified.run(&5), Some(30)); }
#[test]
fn test_reader_t_result() {
let reader: ReaderT<(), Result<i32, &str>> = ReaderT::ok(42);
assert_eq!(reader.run(&()), Ok(42));
let err_reader: ReaderT<(), Result<i32, &str>> = ReaderT::err("error");
assert_eq!(err_reader.run(&()), Err("error"));
}
#[test]
fn test_reader_t_result_universalis_map() {
let reader: ReaderT<(), Result<i32, &str>> = ReaderT::ok(21);
let doubled = reader.map(|x| x * 2);
assert_eq!(doubled.run(&()), Ok(42));
}
#[test]
fn test_reader_t_result_universalis_flat_map() {
let reader: ReaderT<i32, Result<i32, &str>> = ReaderT::new(|env: &i32| Ok(*env));
let chained = reader.flat_map(|val| {
ReaderT::new(move |env: &i32| {
if *env > 0 {
Ok(val * 2)
} else {
Err("negative")
}
})
});
assert_eq!(chained.run(&5), Ok(10));
assert_eq!(chained.run(&0), Err("negative"));
}
#[test]
fn test_reader_t_left_identity() {
let a = 5;
let f = |x: i32| ReaderT::<(), Option<i32>>::pure(x * 2);
let left = ReaderT::<(), Option<i32>>::pure(a).flat_map(f);
let right = f(a);
assert_eq!(left.run(&()), right.run(&()));
}
#[test]
fn test_reader_t_right_identity() {
let m: ReaderT<(), Option<i32>> = ReaderT::pure(42);
let result = m.flat_map(ReaderT::pure);
assert_eq!(result.run(&()), Some(42));
}
#[test]
fn test_reader_t_associativity() {
let f = |x: i32| ReaderT::<(), Option<i32>>::pure(x + 1);
let g = |x: i32| ReaderT::<(), Option<i32>>::pure(x * 2);
let left = ReaderT::<(), Option<i32>>::pure(5).flat_map(f).flat_map(g);
let right = ReaderT::<(), Option<i32>>::pure(5).flat_map(move |x| f(x).flat_map(g));
assert_eq!(left.run(&()), right.run(&()));
}
#[test]
fn test_reader_t_vec() {
let reader: ReaderT<(), Vec<i32>> = ReaderT::singleton(42);
assert_eq!(reader.run(&()), alloc::vec![42]);
let mapped = ReaderT::singleton(21).map(|x| x * 2);
assert_eq!(mapped.run(&()), alloc::vec![42]);
}
}