Capstone — Building a Small Project
Sixteen chapters, two courses — this capstone builds a small, real expression interpreter touching almost all of it: haskell2-7's own GADT expression language, monadic error handling with Either, a custom typeclass for pretty-printing, and a thin IO shell around a fully pure core.
The Expression Language, Extended
Div type-checks fine for any two Expr Int — but dividing by zero is a real, runtime possibility no type signature alone can rule out, which is exactly why evaluation needs to be monadic.
Monadic Evaluation — evalM Returns Either String a
Each sub-evaluation is chained with >>=-powered do-notation — if any sub-expression fails, haskell2-3's own short-circuit-on-Left behavior propagates the error automatically, with no manual error-checking at every step.
A Pretty-Printing Typeclass
A Small IO Shell
evalM and pretty are both completely free of IO in their own types — fully testable with ordinary function calls, no mocking required. Only main itself touches IO, exactly haskell2-4's own recommended shape.
Chapter Attribution
| Capstone piece | Chapter |
|---|---|
| Expr a GADT | haskell2-7 |
| Either-based error handling | haskell1-6 |
| evalM's do-notation / >>= chaining | haskell2-3 |
| Pattern matching on constructors | haskell1-7 |
| The custom Pretty typeclass | haskell1-8, haskell2-5 |
| Pure core / thin IO shell (main only touches IO) | haskell2-4 |
What's Still Out of Scope
Honestly: no real parser or lexer — expressions are built directly in Haskell code, not typed as text by a user, so this isn't a genuine interactive REPL. No monad transformers used here at all, despite haskell2-6 covering them — evalM's own error handling only needs plain Either, a deliberate scope decision, not an oversight. No phantom types beyond the GADT's own per-constructor typing. This capstone proves the pieces fit together, not that the result is a production interpreter.
Coding Challenges
Add a new GADT constructor Mul :: Expr Int -> Expr Int -> Expr Int alongside Add, update evalM and pretty to handle it, and test it with a small expression combining Add and Mul.
📄 View solutionWrite a nested expression that divides by zero INSIDE a larger Add expression (e.g. Add (IntLit 5) (Div (IntLit 10) (IntLit 0))), run it through evalM, and confirm the error propagates out of the whole expression correctly, explaining why in a comment.
📄 View solutionWrite a short paragraph (as a comment) explaining what would need to change in this capstone if it needed to ALSO thread a variable-count "operations performed" counter through evaluation, referencing haskell2-6's own StateT material directly.
📄 View solutionChapter 8 Quick Reference — Haskell Track Complete
- A GADT lets each constructor declare its own specific return type, enabling a genuinely type-safe expression language (haskell2-7)
- evalM's Either-based do-notation chains sub-evaluations, short-circuiting automatically on failure (haskell1-6, haskell2-3)
- A custom Pretty typeclass demonstrates real, practical ad-hoc polymorphism (haskell1-8, haskell2-5)
- evalM and pretty stay completely free of IO — only main touches it, haskell2-4's own recommended shape
- Monad transformers, real parsing, and phantom types are honestly named as still out of scope
- Both Haskell courses are now complete — 16 chapters total, framed throughout as the real origin of ideas already met on this site as Rust's Option/Result/traits.