Session 2: Making illegal states unrepresentable
Objectives
By the end of this session, students can:
- Explain why
Noneis a source of runtime bugs in Python and howOptionmoves that possibility into the type. - Take an
OptionorResultapart withmatchand handle both shapes. - Turn a Python exception into a
Resultvalue withtryand read itserrorKindanderrorMessage. - Define their own algebraic data type with several constructors.
- Read a non-exhaustive-match diagnostic, name the case it reports, and add it.
Prerequisites
Session 1 (values, functions, inference). Python’s own match/case helps but is not required.
Demo script
The centerpiece is the exhaustiveness error naming the exact case the code forgot. Build up to it
with Option and Result, then land it on a hand-written ADT.
- Open the Lesson 3 exercise.
Run
check. The compiler reportsnon-exhaustive match: None is not matched. Ask the class what value is missing before revealing it, then addcase None: "no number"and Run. - Make the point that in Python
int("nope")raises, and a caller has to remember to guard it. Here theNonecase is not a convention to remember, it is a case the compiler refuses to let you skip. - Open the Lesson 4 exercise.
It matches on
parseInt sdirectly, socheckreports a type mismatch: it foundint, not aResult. Wrap the call astry (parseInt s)and Run. Show that the caughtValueErrorreaches theErrorarm as data. - Draw the contrast:
Optionmodels “a value might be absent,”Resultmodels “an operation might fail with a reason.” Both force the caller to answer for the unhappy path. - Now the centerpiece. Open the Lesson 5 exercise.
The
Lighttype has three constructors butactionanswers for two. Runcheckand let the diagnostic speak:non-exhaustive match: Amber is not matched. The compiler names the exact forgotten state. - Add
case Amber: "wait"and Run to get the three expected lines. Then deletecase Green:instead and show the diagnostic now namesGreen. The point is that the checker tracks every constructor, not just the first gap. - Show the emitted Python for the working
Lightprogram. Point out the trailingcase _: raise RuntimeError("non-exhaustive match")guard and explain that because the compiler already proved coverage, that guard can never fire. Exhaustiveness is checked before any Python exists. - Optional stretch: add a fourth constructor
| Flashingto the type without touchingaction. The same diagnostic reappears namingFlashing, showing that widening a type reopens every match over it.
Assigned exercises
- In class: the Lesson 3 exercise and the Lesson 5 exercise, both driven from the demo.
- Homework: the Lesson 4 exercise (wrap the parse in
try). Ask students to readerrorKindanderrorMessageoff the caught value in their answer.
Common misconceptions
- “
Optionis justNonewith extra steps.” Correction: the difference is enforcement. WithOption, the compiler refuses to compile until theNonecase is handled, so the forgotten check cannot happen. - “
tryhere works like Python’stry/exceptblock.” Correction:try eis an expression that produces aResultvalue. There is no jump to a handler. The failure is data you match on in the same expression. - “An ADT is basically a Python
Enum.” Correction: anEnumlists bare names, but ADT constructors can carry data of different types (Circle float,Rect float float), and matching binds that data. - “I can add a
case _:catch-all to silence the exhaustiveness error.” Correction: that works but throws away the guarantee. When you later add a constructor, a realmatchreopens with a diagnostic, while a catch-all silently swallows the new case. - “The exhaustiveness check happens at runtime, like the
RuntimeErrorguard.” Correction: the check runs during type checking, before any Python is emitted. The runtime guard is a backstop that a proven-total match never reaches.
Timing
- 10 min: recap Session 1, then motivate the
Noneproblem in Python. - 20 min: demo steps 1 to 4 (
OptionandResult). - 25 min: demo steps 5 to 8 (the exhaustiveness centerpiece on a hand-written ADT).
- 25 min: students work the Lesson 3 and Lesson 5 exercises in the playground.
- 10 min: recap and assign the Lesson 4 exercise as homework.
Answer keys: answer-keys.md