use monadify::identity::{Identity, IdentityKind};
use monadify::mdo;
use monadify::transformers::writer::{Writer, WriterTKind};
type Traced<A> = Writer<String, A>;
type WKind = WriterTKind<String, IdentityKind>;
fn trace(line: String) -> Traced<()> {
WKind::tell(line)
}
fn add(a: i64, b: i64) -> Traced<i64> {
mdo! { WKind;
_ <- trace(format!("add {} + {} = {}\n", a, b, a + b));
pure(a + b)
}
}
fn mul(a: i64, b: i64) -> Traced<i64> {
mdo! { WKind;
_ <- trace(format!("mul {} * {} = {}\n", a, b, a * b));
pure(a * b)
}
}
fn eval() -> Traced<i64> {
mdo! { WKind;
s <- add(2, 3);
p <- mul(s, 4);
pure(p)
}
}
fn main() {
println!("=== Do-notation with WriterT: Arithmetic Expression Evaluator ===\n");
println!("Test 1: eval_writer_t — produced value only");
let Identity(value) = eval().eval_writer_t();
assert_eq!(value, 20, "eval_writer_t must return 20 for (2+3)*4");
println!(" eval_writer_t -> {} (expected 20)", value);
println!(" PASSED\n");
println!("Test 2: exec_writer_t — accumulated trace log only");
let Identity(trace_log) = eval().exec_writer_t();
assert_eq!(
trace_log, "add 2 + 3 = 5\nmul 5 * 4 = 20\n",
"exec_writer_t must return the two-line trace in evaluation order"
);
println!(" exec_writer_t trace:");
for line in trace_log.lines() {
println!(" {}", line);
}
println!(" PASSED\n");
println!("Test 3: run_writer_t — value and trace together");
let Identity((result, full_trace)) = eval().run_writer_t;
assert_eq!(result, 20, "run_writer_t value must be 20");
assert_eq!(
full_trace, "add 2 + 3 = 5\nmul 5 * 4 = 20\n",
"run_writer_t trace must match the two-line evaluation log"
);
println!(" Result: {}", result);
println!(" Full trace:\n{}", full_trace);
println!(" PASSED\n");
println!("=== All tests passed! ===");
println!("\nKey insight: `mdo!` with `WriterT<String, _>` accumulates a step-by-step trace:");
println!(" * `tell` appends one trace line and yields unit.");
println!(" * `bind` sequences steps and concatenates their logs monoidally.");
println!(" * `run_writer_t` is a field exposing `Identity<(value, log)>` directly.");
println!(" * `eval_writer_t` projects the produced value (trace discarded).");
println!(" * `exec_writer_t` projects the accumulated log (value discarded).");
println!(" * No trace string is ever threaded through any function argument.");
}