use monadify::applicative::kind::Applicative; use monadify::functor::kind::Functor; use monadify::identity::kind::{Identity, IdentityKind}; use monadify::monad::kind::{Bind, Monad}; use monadify::OptionKind;
#[cfg(not(feature = "legacy"))]
use monadify::{MonadReader, ReaderT, ReaderTKind};
#[derive(Clone, Debug, PartialEq)]
struct EnvConfig {
pub val: i32,
}
type TestReader<A> = ReaderT<EnvConfig, IdentityKind, A>; type TestReaderKind = ReaderTKind<EnvConfig, IdentityKind>;
#[test]
fn test_reader_t_kind_functor_map() {
let reader: TestReader<i32> = ReaderT::new(|cfg: EnvConfig| Identity(cfg.val + 1));
let mapped_reader: TestReader<i32> = TestReaderKind::map(reader, |x| x * 2); let result = (mapped_reader.run_reader_t)(EnvConfig { val: 10 });
assert_eq!(result, Identity(22));
}
#[test]
fn test_reader_t_kind_applicative_pure() {
let pure_reader: TestReader<i32> = TestReaderKind::pure(100); let result = (pure_reader.run_reader_t)(EnvConfig { val: 0 });
assert_eq!(result, Identity(100));
}
#[test]
fn test_reader_t_kind_monad_bind() {
let reader1: TestReader<i32> = ReaderT::new(|cfg: EnvConfig| Identity(cfg.val));
let f = |x: i32| -> TestReader<i32> {
ReaderT::new(move |cfg: EnvConfig| Identity(x + cfg.val + 5))
};
let bound_reader: TestReader<i32> = TestReaderKind::bind(reader1, f); let result = (bound_reader.run_reader_t)(EnvConfig { val: 3 });
assert_eq!(result, Identity(11));
}
#[test]
fn test_monad_reader_kind_ask() {
let ask_reader: ReaderT<EnvConfig, IdentityKind, EnvConfig> = <TestReaderKind as MonadReader<EnvConfig, EnvConfig, IdentityKind>>::ask(); let result = (ask_reader.run_reader_t)(EnvConfig { val: 7 });
assert_eq!(result, Identity(EnvConfig { val: 7 }));
}
#[test]
fn test_monad_reader_kind_local() {
let reader: TestReader<i32> = ReaderT::new(|cfg: EnvConfig| Identity(cfg.val * 10));
let modified_reader: TestReader<i32> = TestReaderKind::local(
|mut cfg: EnvConfig| {
cfg.val += 1;
cfg
},
reader,
); let result = (modified_reader.run_reader_t)(EnvConfig { val: 2 });
assert_eq!(result, Identity(30));
}
fn run_test_reader<A: PartialEq + std::fmt::Debug>(reader: TestReader<A>, env: EnvConfig) -> A {
(reader.run_reader_t)(env).0
}
fn assert_readers_eq<A: PartialEq + std::fmt::Debug + Clone>(
reader1: TestReader<A>,
reader2: TestReader<A>,
env: EnvConfig,
) {
let val1 = (reader1.run_reader_t)(env.clone()).0;
let val2 = (reader2.run_reader_t)(env).0;
assert_eq!(val1, val2);
}
#[test]
fn test_reader_t_kind_monad_join() {
let env = EnvConfig { val: 10 };
let mma_reader: TestReader<TestReader<i32>> = TestReaderKind::pure(TestReaderKind::pure(100));
let joined_reader: TestReader<i32> = TestReaderKind::join(mma_reader); assert_eq!(run_test_reader(joined_reader, env.clone()), 100);
let mma_reader_env_dependent: TestReader<TestReader<i32>> =
ReaderT::new(move |cfg1: EnvConfig| {
Identity(ReaderT::new(move |cfg2: EnvConfig| {
Identity(cfg1.val + cfg2.val)
}))
});
let joined_reader_env: TestReader<i32> = TestReaderKind::join(mma_reader_env_dependent); assert_eq!(
run_test_reader(joined_reader_env, env.clone()),
env.val + env.val
);
}
#[test]
fn test_reader_t_kind_monad_law_left_identity() {
let env = EnvConfig { val: 5 };
let a = 10;
let f = |x: i32| -> TestReader<String> {
ReaderT::new(move |cfg: EnvConfig| Identity((x + cfg.val).to_string()))
};
let lhs: TestReader<String> = TestReaderKind::bind(TestReaderKind::pure(a), f.clone()); let rhs: TestReader<String> = f(a);
assert_readers_eq(lhs.clone(), rhs.clone(), env.clone());
let expected_val_rhs = ((f(a)).run_reader_t)(env).0;
assert_eq!(expected_val_rhs, "15");
}
#[test]
fn test_reader_t_kind_monad_law_right_identity() {
let env = EnvConfig { val: 7 };
let m_orig: TestReader<i32> = ReaderT::new(move |cfg: EnvConfig| Identity(cfg.val * 2));
let pure_fn = |x: i32| TestReaderKind::pure(x);
let lhs: TestReader<i32> = TestReaderKind::bind(m_orig.clone(), pure_fn); let rhs: TestReader<i32> = m_orig.clone();
assert_readers_eq(lhs.clone(), rhs.clone(), env.clone());
let val_m_orig = (m_orig.run_reader_t)(env).0;
assert_eq!(val_m_orig, 14);
}
#[test]
fn test_reader_t_kind_monad_law_associativity() {
let env = EnvConfig { val: 3 };
let m_orig: TestReader<i32> = ReaderT::new(move |cfg: EnvConfig| Identity(cfg.val + 1));
let f =
|x: i32| -> TestReader<i32> { ReaderT::new(move |cfg: EnvConfig| Identity(x * cfg.val)) };
let g = |y: i32| -> TestReader<String> {
ReaderT::new(move |cfg: EnvConfig| Identity((y + cfg.val).to_string()))
};
let lhs: TestReader<String> =
TestReaderKind::bind(TestReaderKind::bind(m_orig.clone(), f.clone()), g.clone());
let f_clone_for_rhs = f.clone();
let g_clone_for_rhs = g.clone();
let composed_func = move |x_val: i32| -> TestReader<String> {
TestReaderKind::bind(f_clone_for_rhs(x_val), g_clone_for_rhs.clone()) };
let rhs: TestReader<String> = TestReaderKind::bind(m_orig.clone(), composed_func);
assert_readers_eq(lhs.clone(), rhs.clone(), env.clone());
let val_lhs = (lhs.run_reader_t)(env).0;
assert_eq!(val_lhs, "15");
}
fn run_kind_reader_t_option<
R: Clone + 'static,
A: PartialEq + std::fmt::Debug + Clone + 'static,
>(
reader: ReaderT<R, OptionKind, A>, env: R,
) -> Option<A> {
(reader.run_reader_t)(env)
}
#[test]
fn test_kind_reader_t_monad_laws_option_inner() {
type Env = i32;
type A = i32;
type B = String;
type C = usize;
let env_val: Env = 10;
let a_val: A = 5;
let f_opt = move |x: A| -> ReaderT<Env, OptionKind, B> {
ReaderT::new(move |env: Env| -> Option<B> {
if x > 0 && env > 0 {
Some(format!("f_opt: val={}, env={}", x, env))
} else {
None
}
})
};
let g_opt = move |s: B| -> ReaderT<Env, OptionKind, C> {
ReaderT::new(move |env: Env| -> Option<C> {
if !s.is_empty() && env > 0 {
Some(s.len() + env as usize)
} else {
None
}
})
};
let m_opt_some: ReaderT<Env, OptionKind, A> = ReaderT::new(move |env: Env| if env > 0 { Some(a_val + env) } else { None });
let m_opt_none: ReaderT<Env, OptionKind, A> = ReaderT::new(move |_env: Env| None::<A>);
let pure_a_opt: ReaderT<Env, OptionKind, A> = ReaderTKind::<Env, OptionKind>::pure(a_val); let left_law1_opt = ReaderTKind::<Env, OptionKind>::bind(pure_a_opt.clone(), f_opt.clone()); let right_law1_opt = f_opt(a_val);
assert_eq!(
run_kind_reader_t_option(left_law1_opt.clone(), env_val), run_kind_reader_t_option(right_law1_opt.clone(), env_val) );
let left_law2_opt = ReaderTKind::<Env, OptionKind>::bind(m_opt_some.clone(), |x: A| {
ReaderTKind::<Env, OptionKind>::pure(x)
}); let right_law2_opt = m_opt_some.clone();
assert_eq!(
run_kind_reader_t_option(left_law2_opt, env_val), run_kind_reader_t_option(right_law2_opt, env_val) );
let left_law3_opt = ReaderTKind::<Env, OptionKind>::bind(
ReaderTKind::<Env, OptionKind>::bind(m_opt_some.clone(), f_opt.clone()),
g_opt.clone(),
); let right_law3_opt = ReaderTKind::<Env, OptionKind>::bind(m_opt_some.clone(), move |x: A| {
ReaderTKind::<Env, OptionKind>::bind(f_opt(x), g_opt.clone())
}); assert_eq!(
run_kind_reader_t_option(left_law3_opt.clone(), env_val), run_kind_reader_t_option(right_law3_opt.clone(), env_val) );
let val_for_f_none: A = -1;
let pure_val_for_f_none: ReaderT<Env, OptionKind, A> = ReaderTKind::<Env, OptionKind>::pure(val_for_f_none); let left_law1_f_none =
ReaderTKind::<Env, OptionKind>::bind(pure_val_for_f_none.clone(), f_opt.clone()); let right_law1_f_none = f_opt(val_for_f_none);
assert_eq!(
run_kind_reader_t_option(left_law1_f_none.clone(), env_val), run_kind_reader_t_option(right_law1_f_none.clone(), env_val) );
assert_eq!(
run_kind_reader_t_option(right_law1_f_none.clone(), env_val),
None
);
let left_law2_none = ReaderTKind::<Env, OptionKind>::bind(m_opt_none.clone(), |x: A| {
ReaderTKind::<Env, OptionKind>::pure(x)
}); let right_law2_none = m_opt_none.clone();
assert_eq!(
run_kind_reader_t_option(left_law2_none.clone(), env_val), run_kind_reader_t_option(right_law2_none.clone(), env_val) );
assert_eq!(
run_kind_reader_t_option(right_law2_none.clone(), env_val),
None
);
let left_law3_none = ReaderTKind::<Env, OptionKind>::bind(
ReaderTKind::<Env, OptionKind>::bind(m_opt_none.clone(), f_opt.clone()),
g_opt.clone(),
); let right_law3_none = ReaderTKind::<Env, OptionKind>::bind(m_opt_none.clone(), move |x: A| {
ReaderTKind::<Env, OptionKind>::bind(f_opt(x), g_opt.clone())
}); assert_eq!(
run_kind_reader_t_option(left_law3_none.clone(), env_val), run_kind_reader_t_option(right_law3_none.clone(), env_val) );
assert_eq!(
run_kind_reader_t_option(right_law3_none.clone(), env_val),
None
); }