use monadify::applicative::kind::Applicative;
use monadify::kind_based::kind::ResultKind;
use monadify::mdo;
use monadify::monad::kind::Bind;
use proptest::prelude::*;
use super::super::proptest_laws::arb_result_i32_string;
#[test]
fn result_mdo_two_bindings_both_ok() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Ok(2);
y <- Ok(3);
ResultKind::<String>::pure(x + y)
};
assert_eq!(result, Ok(5));
}
#[test]
fn result_mdo_three_bindings_all_ok() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Ok(1);
y <- Ok(2);
z <- Ok(3);
ResultKind::<String>::pure(x + y + z)
};
assert_eq!(result, Ok(6));
}
#[test]
fn result_mdo_short_circuit_first_binding_err() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Err::<i32, String>("first-err".to_string());
y <- Ok::<i32, String>(3);
ResultKind::<String>::pure(x + y)
};
assert_eq!(result, Err("first-err".to_string()));
}
#[test]
fn result_mdo_short_circuit_second_binding_err() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Ok(2);
y <- Err::<i32, String>("second-err".to_string());
ResultKind::<String>::pure(x + y)
};
assert_eq!(result, Err("second-err".to_string()));
}
#[test]
fn result_mdo_short_circuit_middle_of_three() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Ok(1);
y <- Err::<i32, String>("mid-err".to_string());
z <- Ok(3);
ResultKind::<String>::pure(x + y + z)
};
assert_eq!(result, Err("mid-err".to_string()));
}
#[test]
fn result_mdo_first_err_wins() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Err::<i32, String>("first".to_string());
y <- Err::<i32, String>("second".to_string());
ResultKind::<String>::pure(x + y)
};
assert_eq!(result, Err("first".to_string()));
}
#[test]
fn result_mdo_equivalence_both_ok() {
let ma: Result<i32, String> = Ok(2);
let mb: Result<i32, String> = Ok(3);
let ma_lhs = ma.clone();
let mb_lhs = mb.clone();
let lhs: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- ma_lhs;
y <- mb_lhs;
ResultKind::<String>::pure(x + y)
};
let rhs: Result<i32, String> = ResultKind::<String>::bind(ma, move |x| {
ResultKind::<String>::bind(mb.clone(), move |y| ResultKind::<String>::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, Ok(5));
}
#[test]
fn result_mdo_equivalence_first_err() {
let ma: Result<i32, String> = Err("e".to_string());
let mb: Result<i32, String> = Ok(3);
let ma_lhs = ma.clone();
let mb_lhs = mb.clone();
let lhs: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- ma_lhs;
y <- mb_lhs;
ResultKind::<String>::pure(x + y)
};
let rhs: Result<i32, String> = ResultKind::<String>::bind(ma, move |x| {
ResultKind::<String>::bind(mb.clone(), move |y| ResultKind::<String>::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, Err("e".to_string()));
}
#[test]
fn result_mdo_equivalence_second_err() {
let ma: Result<i32, String> = Ok(7);
let mb: Result<i32, String> = Err("e".to_string());
let ma_lhs = ma.clone();
let mb_lhs = mb.clone();
let lhs: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- ma_lhs;
y <- mb_lhs;
ResultKind::<String>::pure(x + y)
};
let rhs: Result<i32, String> = ResultKind::<String>::bind(ma, move |x| {
ResultKind::<String>::bind(mb.clone(), move |y| ResultKind::<String>::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, Err("e".to_string()));
}
#[test]
fn result_mdo_equivalence_both_err() {
let ma: Result<i32, String> = Err("first".to_string());
let mb: Result<i32, String> = Err("second".to_string());
let ma_lhs = ma.clone();
let mb_lhs = mb.clone();
let lhs: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- ma_lhs;
y <- mb_lhs;
ResultKind::<String>::pure(x + y)
};
let rhs: Result<i32, String> = ResultKind::<String>::bind(ma, move |x| {
ResultKind::<String>::bind(mb.clone(), move |y| ResultKind::<String>::pure(x + y))
});
assert_eq!(lhs, rhs);
assert_eq!(lhs, Err("first".to_string()));
}
proptest! {
#![proptest_config(ProptestConfig { cases: 256, ..ProptestConfig::default() })]
#[test]
fn result_mdo_equivalence_prop(
ma in arb_result_i32_string(),
mb in arb_result_i32_string(),
) {
let ma_lhs = ma.clone();
let mb_lhs = mb.clone();
let lhs: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- ma_lhs;
y <- mb_lhs;
ResultKind::<String>::pure(x.wrapping_add(y))
};
let rhs: Result<i32, String> = ResultKind::<String>::bind(ma, move |x| {
ResultKind::<String>::bind(mb.clone(), move |y| {
ResultKind::<String>::pure(x.wrapping_add(y))
})
});
prop_assert_eq!(lhs, rhs);
}
}
#[test]
fn result_mdo_let_binding_inside_block() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Ok(10);
let doubled = x * 2;
ResultKind::<String>::pure(doubled)
};
assert_eq!(result, Ok(20));
}
#[test]
fn result_mdo_let_binding_combines_two_values() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Ok(4);
y <- Ok(6);
let sum = x + y;
ResultKind::<String>::pure(sum * 2)
};
assert_eq!(result, Ok(20));
}
#[test]
fn result_mdo_bare_expr_sequencing_ok() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Ok(5);
Ok::<(), String>(());
ResultKind::<String>::pure(x * 3)
};
assert_eq!(result, Ok(15));
}
#[test]
fn result_mdo_bare_expr_sequencing_err_short_circuits() {
let result: Result<i32, String> = mdo! {
ResultKind::<String>;
x <- Ok(5);
Err::<(), String>("bare-err".to_string());
ResultKind::<String>::pure(x * 3)
};
assert_eq!(result, Err("bare-err".to_string()));
}
#[test]
fn result_mdo_let_and_bare_expr_combined() {
let result: Result<String, String> = mdo! {
ResultKind::<String>;
x <- Ok::<i32, String>(7);
let label = "value";
Ok::<(), String>(());
ResultKind::<String>::pure(format!("{}: {}", label, x))
};
assert_eq!(result, Ok("value: 7".to_string()));
}