use monadify::applicative::kind::Applicative;
use monadify::kind_based::kind::VecKind;
use monadify::mdo;
use monadify::monad::kind::Bind;
use proptest::prelude::*;
use super::super::proptest_laws::arb_vec_i32;
#[test]
fn vec_mdo_cartesian_product_two_bindings() {
let result: Vec<i32> = mdo! {
VecKind;
x <- vec![1i32, 2];
y <- vec![10i32, 20];
VecKind::pure(x + y)
};
assert_eq!(result, vec![11, 21, 12, 22]);
}
#[test]
fn vec_mdo_cartesian_product_three_bindings() {
let result: Vec<i32> = mdo! {
VecKind;
x <- vec![0i32, 1];
y <- vec![0i32, 10];
z <- vec![0i32, 100];
VecKind::pure(x + y + z)
};
assert_eq!(result, vec![0, 100, 10, 110, 1, 101, 11, 111]);
}
#[test]
fn vec_mdo_empty_first_binding_short_circuits() {
let result: Vec<i32> = mdo! {
VecKind;
x <- Vec::<i32>::new();
y <- vec![10i32, 20];
VecKind::pure(x + y)
};
assert_eq!(result, vec![]);
}
#[test]
fn vec_mdo_empty_second_binding_short_circuits() {
let result: Vec<i32> = mdo! {
VecKind;
x <- vec![1i32, 2];
y <- Vec::<i32>::new();
VecKind::pure(x + y)
};
assert_eq!(result, vec![]);
}
#[test]
fn vec_mdo_empty_middle_of_three_short_circuits() {
let result: Vec<i32> = mdo! {
VecKind;
x <- vec![1i32, 2];
y <- Vec::<i32>::new();
z <- vec![100i32];
VecKind::pure(x + y + z)
};
assert_eq!(result, vec![]);
}
#[test]
fn vec_mdo_guard_filters_evens() {
let result: Vec<i32> = mdo! {
VecKind;
x <- vec![1i32, 2, 3, 4];
guard(x % 2 == 0);
VecKind::pure(x)
};
assert_eq!(result, vec![2, 4]);
}
#[test]
fn vec_mdo_guard_all_filtered_out_returns_empty() {
let result: Vec<i32> = mdo! {
VecKind;
x <- vec![1i32, 3, 5, 7];
guard(x % 2 == 0);
VecKind::pure(x)
};
assert_eq!(result, vec![]);
}
#[test]
fn vec_mdo_guard_literal_true_passes_all() {
let result: Vec<i32> = mdo! {
VecKind;
x <- vec![1i32, 2, 3];
guard(true);
VecKind::pure(x)
};
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn vec_mdo_guard_literal_false_yields_empty() {
let result: Vec<i32> = mdo! {
VecKind;
x <- vec![1i32, 2, 3];
guard(false);
VecKind::pure(x)
};
assert_eq!(result, vec![]);
}
#[test]
fn vec_mdo_guard_nested_filter_pairs_sum_to_4() {
let result: Vec<(i32, i32)> = mdo! {
VecKind;
x <- vec![1i32, 2, 3];
y <- vec![1i32, 2, 3];
guard(x + y == 4);
VecKind::pure((x, y))
};
assert_eq!(result, vec![(1, 3), (2, 2), (3, 1)]);
}
#[test]
fn vec_mdo_multiple_guards_composed() {
let result: Vec<(i32, i32)> = mdo! {
VecKind;
x <- vec![1i32, 2, 3, 4];
y <- vec![1i32, 2, 3, 4];
guard(x != y);
guard(x + y > 5);
VecKind::pure((x, y))
};
assert_eq!(result, vec![(2, 4), (3, 4), (4, 2), (4, 3)]);
}
#[test]
fn vec_mdo_equivalence_both_nonempty() {
let va: Vec<i32> = vec![1, 2];
let vb: Vec<i32> = vec![10, 20];
let va_lhs = va.clone();
let vb_lhs = vb.clone();
let lhs: Vec<i32> = mdo! {
VecKind;
x <- va_lhs;
y <- vb_lhs;
VecKind::pure(x + y)
};
let rhs: Vec<i32> = VecKind::bind(va.clone(), move |x| {
VecKind::bind(vb.clone(), move |y| VecKind::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, vec![11, 21, 12, 22]);
}
#[test]
fn vec_mdo_equivalence_first_empty() {
let va: Vec<i32> = vec![];
let vb: Vec<i32> = vec![10, 20];
let va_lhs = va.clone();
let vb_lhs = vb.clone();
let lhs: Vec<i32> = mdo! {
VecKind;
x <- va_lhs;
y <- vb_lhs;
VecKind::pure(x + y)
};
let rhs: Vec<i32> = VecKind::bind(va.clone(), move |x| {
VecKind::bind(vb.clone(), move |y| VecKind::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, vec![]);
}
#[test]
fn vec_mdo_equivalence_second_empty() {
let va: Vec<i32> = vec![1, 2];
let vb: Vec<i32> = vec![];
let va_lhs = va.clone();
let vb_lhs = vb.clone();
let lhs: Vec<i32> = mdo! {
VecKind;
x <- va_lhs;
y <- vb_lhs;
VecKind::pure(x + y)
};
let rhs: Vec<i32> = VecKind::bind(va.clone(), move |x| {
VecKind::bind(vb.clone(), move |y| VecKind::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, vec![]);
}
#[test]
fn vec_mdo_equivalence_both_empty() {
let va: Vec<i32> = vec![];
let vb: Vec<i32> = vec![];
let va_lhs = va.clone();
let vb_lhs = vb.clone();
let lhs: Vec<i32> = mdo! {
VecKind;
x <- va_lhs;
y <- vb_lhs;
VecKind::pure(x + y)
};
let rhs: Vec<i32> = VecKind::bind(va.clone(), move |x| {
VecKind::bind(vb.clone(), move |y| VecKind::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, vec![]);
}
proptest! {
#![proptest_config(ProptestConfig { cases: 256, ..ProptestConfig::default() })]
#[test]
fn vec_mdo_equivalence_prop(
va in arb_vec_i32(),
vb in arb_vec_i32(),
) {
let va_lhs = va.clone();
let vb_lhs = vb.clone();
let lhs: Vec<i32> = mdo! {
VecKind;
x <- va_lhs;
y <- vb_lhs;
VecKind::pure(x.wrapping_add(y))
};
let rhs: Vec<i32> = VecKind::bind(va.clone(), move |x| {
VecKind::bind(vb.clone(), move |y| VecKind::pure(x.wrapping_add(y)))
});
prop_assert_eq!(lhs, rhs);
}
}