use monadify::applicative::kind::Applicative;
use monadify::identity::{Identity, IdentityKind};
use monadify::mdo;
use monadify::monad::kind::Bind;
use monadify::transformers::state::{State, StateTKind};
use proptest::prelude::*;
type StKind = StateTKind<i32, IdentityKind>;
type Counter<A> = State<i32, A>;
fn get() -> Counter<i32> {
StKind::get()
}
fn put(s: i32) -> Counter<()> {
StKind::put(s)
}
fn run_id<A>(st: Counter<A>, s0: i32) -> (A, i32) {
let Identity(pair) = (st.run_state_t)(s0);
pair
}
#[test]
fn state_mdo_threads_and_updates() {
let comp: Counter<i32> = mdo! {
StKind;
x <- get();
_ <- put(x + 1);
y <- get();
StKind::pure(x + y)
};
assert_eq!(run_id(comp, 10), (21, 11));
}
#[test]
fn state_mdo_uses_modify() {
let comp: Counter<i32> = mdo! {
StKind;
_ <- StKind::modify(|s| s * 2);
x <- get();
StKind::pure(x + 1)
};
assert_eq!(run_id(comp, 5), (11, 10));
}
#[test]
fn state_mdo_equivalent_to_manual_bind() {
let via_mdo: Counter<i32> = mdo! {
StKind;
x <- get();
_ <- put(x + 1);
y <- get();
StKind::pure(x + y)
};
let via_bind: Counter<i32> = StKind::bind(get(), |x| {
StKind::bind(put(x + 1), move |_| {
StKind::bind(get(), move |y| StKind::pure(x + y))
})
});
for s0 in [-5, 0, 3, 10, 100] {
assert_eq!(run_id(via_mdo.clone(), s0), run_id(via_bind.clone(), s0));
}
}
#[test]
fn state_mdo_four_binding_chain() {
let comp: Counter<i32> = mdo! {
StKind;
a <- get();
_ <- put(a + 1);
b <- get();
_ <- put(b + 1);
c <- get();
StKind::pure(a + b + c)
};
assert_eq!(run_id(comp, 0), (3, 2));
}
proptest! {
#![proptest_config(ProptestConfig { cases: 256, ..ProptestConfig::default() })]
#[test]
fn state_mdo_matches_manual_over_generated_states(s0 in any::<i32>(), d in any::<i32>()) {
let via_mdo: Counter<i32> = mdo! {
StKind;
x <- get();
_ <- StKind::modify(move |s| s.wrapping_add(d));
y <- get();
StKind::pure(x.wrapping_add(y))
};
let via_bind: Counter<i32> = StKind::bind(get(), move |x| {
StKind::bind(
StKind::modify(move |s| s.wrapping_add(d)),
move |_| StKind::bind(get(), move |y| StKind::pure(x.wrapping_add(y))),
)
});
prop_assert_eq!(run_id(via_mdo, s0), run_id(via_bind, s0));
}
}