Challenge 3: Adding an Operations-Performed Counter — Possible Solution ==================================================================== -- Currently, evalM :: Expr a -> Either String a carries exactly ONE -- effect: the possibility of failure (via Left). Adding a genuine -- running counter of how many operations (Add, Mul, Div, etc.) have -- been performed during evaluation means evalM would now need TWO -- effects at once: the existing error-handling AND a piece of -- threaded, updatable state (the counter) -- exactly the situation -- haskell2-6 named as the real motivation for monad transformers. -- -- The evalM :: Expr a -> Either String a signature would need to -- become something like: -- -- evalM :: Expr a -> State Int (Either String a) -- -- or, combined properly as ONE unified monad instead of nesting: -- evalM :: Expr a -> StateT Int (Either String) a -- -- Using StateT Int (Either String) a specifically (rather than the -- plain nested version) would restore haskell2-3's own clean >>= -- chaining across BOTH effects at once, exactly the problem -- haskell2-6's own MaybeT/ExceptT/StateT material was built to solve. -- Every recursive call to evalM inside Add/Mul/Div/If's own case -- would need to increment the counter as part of its own do-block -- (something like `modify (+1)` before or after evaluating), and the -- final result would need to be unwrapped once at the very end, in -- main, using something like runStateT (evalM expr) 0. -- -- Honestly, per haskell2-6's own central point: this genuinely adds -- real ceremony (lift/liftIO-style noise, though here it would be -- State-specific rather than IO-specific) that the current, simpler -- Either-only version deliberately avoids -- which is exactly why the -- capstone's own "still out of scope" section named monad transformers -- as a deliberate scope decision rather than an oversight. WHY THIS WORKS AS AN ANSWER ------------------------------ This correctly identifies that adding a state-threading requirement introduces a SECOND effect alongside error handling, names StateT combined with Either as the appropriate transformer for this exact situation, and ties the added complexity directly back to haskell2-6's own honest chapter about where transformer stacks get genuinely harder -- matching the capstone's own stated reason for leaving this out of scope.