Session 4: The Pyfun workflow
Objectives
By the end of this session, students can:
- Use a typed hole to sketch a program and let the compiler report the type and fitting names at each gap.
- Fill a pipeline inward from the types the holes report, without guessing function signatures.
- Declare a deliberate local accumulator with
let mutand reassign it with<-. - Explain why immutability is the default and mutation is opt-in and visible.
- Read an inferred effect, assert purity with
let pure, and fix a failed purity assertion by moving the effect to the call site.
Prerequisites
Sessions 1 through 3 (values, functions, match, records, collections). Students have already seen
holes in passing when the earlier exercises reported a hole type.
Demo script
The thread is the day-to-day loop of writing Pyfun: sketch with holes, let types guide the fill, reach for mutation only when it is genuinely clearest, and let the compiler track effects for you.
- Open the Lesson 9 exercise.
Run
check. Two holes report at once. Read the?rendernote aloud: it has typeint -> 'aand suggestsString.fromIntamong the fits. This is type-driven development in one screen. - Fill
?missingwith0and?renderwithString.fromInt, then Run to get10and0. Make the point that you never looked up a signature: each hole reported exactly what belonged there. - Emphasize that a hole blocks compilation by design. Nothing runs until every blank is filled, so a
half-written program cannot accidentally execute. Contrast with a Python
passorTODO, which runs and fails later. - Move to mutation. Open the Lesson 10 exercise.
It reassigns
accwith<-but declared it with a plainlet. Runcheckand read the diagnostic: it saysaccis immutable and points tolet mut. Addmutand Run to get120. - Show the emitted Python: the block became the plain statement sequence you would write by hand,
total = ...reassigned in place. The<-arrow is what makes mutation visible in the source, and it is scoped to the block. Note there is nofororwhile, solet mutis for a genuine accumulator, not for iterating a collection. - Move to effects. Open the Lesson 11 exercise.
greetis declaredpurebut prints inside its body. Runcheckand read the diagnostic:greet is declared pure but performs io, pointing at theprintline. - Fix it the right way. Make
greetreturn the string (let pure greet name = f"Hello, {name}") and move theprintto the call site (print (greet "ada")). Run. Make the point: the fix is to separate the calculating part, which stays provably pure, from the part that talks to the world, which now says so. - To reinforce inference, add a second function that calls
greetand prints, and show that itsioeffect is inferred without any annotation, while alet pureon it would be rejected. Effect tracking propagates outward on its own.
Assigned exercises
- In class: the Lesson 9 exercise and the Lesson 10 exercise, both driven from the demo.
- Homework: the Lesson 11 exercise (make
greetpure by returning the string and printing at the call site).
Common misconceptions
- “A hole is like
pass, so the program still runs with a blank.” Correction: a hole blocks compilation. Nothing executes until every hole is filled, which is what makes it safe to sketch. - “I have to know a function’s signature before I can use it.” Correction: the hole reports the type it expects and lists in-scope names that fit. You fill inward from what the compiler tells you.
- “
let mutmeans I should reach for it whenever I loop.” Correction: there is nofororwhile. Iterating a collection isList.foldand friends.let mutis for a local accumulator built from a few explicit steps. - “
acc <- xis the same asacc = x, just different syntax.” Correction:<-reassigns an existingmutbinding and is visible on purpose. A plainletcannot be reassigned at all. - “Marking something
puremakes it pure.” Correction:let pureis an assertion the compiler checks. If the body performs an effect, it is a compile error. You cannot declare away an effect.
Timing
- 10 min: recap Session 3, then frame the write-with-holes workflow.
- 20 min: demo steps 1 to 3 (typed holes and type-driven development).
- 20 min: demo steps 4 to 5 (deliberate mutation).
- 20 min: demo steps 6 to 8 (inferred effects and
let pure). - 15 min: students work the Lesson 9 and Lesson 10 exercises in the playground.
- 5 min: recap and assign the Lesson 11 exercise as homework.
Answer keys: answer-keys.md