use monadify::applicative::kind::Applicative;
use monadify::kind_based::kind::OptionKind;
use monadify::mdo;
use monadify::monad::kind::Bind;
use proptest::prelude::*;
use super::super::proptest_laws::arb_option_i32;
#[test]
fn option_mdo_two_bindings_both_some() {
let result: Option<i32> = mdo! {
OptionKind;
x <- Some(2);
y <- Some(3);
OptionKind::pure(x + y)
};
assert_eq!(result, Some(5));
}
#[test]
fn option_mdo_three_bindings_all_some() {
let result: Option<i32> = mdo! {
OptionKind;
x <- Some(1);
y <- Some(2);
z <- Some(3);
OptionKind::pure(x + y + z)
};
assert_eq!(result, Some(6));
}
#[test]
fn option_mdo_short_circuit_first_binding_none() {
let result: Option<i32> = mdo! {
OptionKind;
x <- None::<i32>;
y <- Some(3);
OptionKind::pure(x + y)
};
assert_eq!(result, None);
}
#[test]
fn option_mdo_short_circuit_second_binding_none() {
let result: Option<i32> = mdo! {
OptionKind;
x <- Some(2);
y <- None::<i32>;
OptionKind::pure(x + y)
};
assert_eq!(result, None);
}
#[test]
fn option_mdo_short_circuit_middle_of_three() {
let result: Option<i32> = mdo! {
OptionKind;
x <- Some(1);
y <- None::<i32>;
z <- Some(3);
OptionKind::pure(x + y + z)
};
assert_eq!(result, None);
}
#[test]
fn option_mdo_equivalence_both_some() {
let ma: Option<i32> = Some(2);
let mb: Option<i32> = Some(3);
let lhs: Option<i32> = mdo! {
OptionKind;
x <- ma;
y <- mb;
OptionKind::pure(x + y)
};
let rhs: Option<i32> = OptionKind::bind(ma.clone(), move |x| {
OptionKind::bind(mb.clone(), move |y| OptionKind::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, Some(5));
}
#[test]
fn option_mdo_equivalence_first_none() {
let ma: Option<i32> = None;
let mb: Option<i32> = Some(3);
let lhs: Option<i32> = mdo! {
OptionKind;
x <- ma;
y <- mb;
OptionKind::pure(x + y)
};
let rhs: Option<i32> = OptionKind::bind(ma.clone(), move |x| {
OptionKind::bind(mb.clone(), move |y| OptionKind::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, None);
}
#[test]
fn option_mdo_equivalence_second_none() {
let ma: Option<i32> = Some(7);
let mb: Option<i32> = None;
let lhs: Option<i32> = mdo! {
OptionKind;
x <- ma;
y <- mb;
OptionKind::pure(x + y)
};
let rhs: Option<i32> = OptionKind::bind(ma.clone(), move |x| {
OptionKind::bind(mb.clone(), move |y| OptionKind::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, None);
}
#[test]
fn option_mdo_equivalence_both_none() {
let ma: Option<i32> = None;
let mb: Option<i32> = None;
let lhs: Option<i32> = mdo! {
OptionKind;
x <- ma;
y <- mb;
OptionKind::pure(x + y)
};
let rhs: Option<i32> = OptionKind::bind(ma.clone(), move |x| {
OptionKind::bind(mb.clone(), move |y| OptionKind::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, None);
}
#[test]
fn option_mdo_guard_condition_true_passes_through() {
let result: Option<i32> = mdo! {
OptionKind;
x <- Some(4);
guard(x > 0);
OptionKind::pure(x)
};
assert_eq!(result, Some(4));
}
#[test]
fn option_mdo_guard_condition_false_short_circuits() {
let result: Option<i32> = mdo! {
OptionKind;
x <- Some(4);
guard(x < 0);
OptionKind::pure(x)
};
assert_eq!(result, None);
}
#[test]
fn option_mdo_guard_literal_false() {
let result: Option<i32> = mdo! {
OptionKind;
x <- Some(99);
guard(false);
OptionKind::pure(x)
};
assert_eq!(result, None);
}
#[test]
fn option_mdo_guard_literal_true() {
let result: Option<i32> = mdo! {
OptionKind;
x <- Some(42);
guard(true);
OptionKind::pure(x)
};
assert_eq!(result, Some(42));
}
proptest! {
#![proptest_config(ProptestConfig { cases: 256, ..ProptestConfig::default() })]
#[test]
fn option_mdo_equivalence_prop(
ma in arb_option_i32(),
mb in arb_option_i32(),
) {
let lhs: Option<i32> = mdo! {
OptionKind;
x <- ma;
y <- mb;
OptionKind::pure(x.wrapping_add(y))
};
let rhs: Option<i32> = OptionKind::bind(ma.clone(), move |x| {
OptionKind::bind(mb.clone(), move |y| OptionKind::pure(x.wrapping_add(y)))
});
prop_assert_eq!(lhs, rhs);
}
}