As a program grows, the same little job turns up again and again: draw the border, show the score, play the sound. Copy it into every place that needs it and two things go wrong. The program bloats, and the day you want to change that job you have to find every copy.
Here both copies do the same job and should change together. Giving that job one home makes the change easier to check.
Write it once, call it
DEFINE divider
SHOW "----------"
END
CALL divider
SHOW "Top news"---------- Top news
DEFINE gives a job a name and holds the work under it. CALL runs that job. When the job
finishes, the program carries on from the line after the CALL.
The order the lines run in is not the order you read them, which is the part a listing cannot show you:
| Step | What runs | Shown |
|---|---|---|
| 1 | CALL divider | — |
| 2 | the SHOW inside the job | ---------- |
| 3 | the job ends, back to the CALL | — |
| 4 | SHOW "Top news" | Top news |
The program hops into the job at step 2 and comes back at step 3. It picks up exactly where it left off, on the line after the one that called.
The name is the point. divider says what the block is for, so you can read CALL divider
without going to look at what it does.
The point is reuse
You can call one job as many times as you like, and that is where writing it once earns its keep:
DEFINE divider
SHOW "----------"
END
CALL divider
SHOW "Top news"
CALL divider
SHOW "More news"
CALL divider---------- Top news ---------- More news ----------
The divider appears three times on screen and exists once in the program. That gives us one place
to change this shared job. Want stars instead of dashes? Change the one line inside divider, and
all three follow.
As programs grow into real games, naming the jobs keeps them readable instead of a wall of repetition: draw the player, check for a hit, add to the score.
Before editing, predict what happens if we replace the dashes inside divider with
stars. Then consider a different request: only the divider above “Top news” should
change. Would the same edit meet that request?
Check which callers change
Changing the shared job changes all three dividers. That meets the first request, but not the second. For just one different divider, we need to distinguish that call: we could keep a separate short job, or pass in a value once we have learnt how. The intended behaviour tells us whether sharing still helps.
When two jobs should stay separate
Suppose the title needs a wide divider but a small score panel needs a short one. Would changing every divider together still help? We might give the routine a width, which the next lesson shows us how to do. We might instead keep two short routines with clear names if their purposes are growing apart.
A little duplication can be easier to understand than one routine with many special cases. We can wait until we know what really stays the same. Naming a job also helps us explain and check it, even when we only call it once.
A call is not a promise of free work. In a machine lesson we will account for passing values, entering the routine and returning. That cost may be worth the clearer code; we measure it when a game’s budget makes it matter.
Leaving a note
A good name says what a job is for. Where a name is not enough, write a note. Anything
after a # is for people to read, and the computer skips it:
# The dashes have to match the width of the score panel.
DEFINE divider
SHOW "----------"
ENDNotes are worth most where they say why, not what. SHOW "----------" already says
what the line does. Why ten dashes is the part a reader cannot work out.
When it’s wrong, see why
- The call names an unknown job. Check that the name matches a definition. A real language may report an error; a spelling mistake does not reliably mean silence.
- You changed one thing and several changed. That is the other side of writing it once: every caller gets the change. If one caller wanted something different, it wanted its own job, or a job you can tell what to do.
- You cannot tell what a job does from its name. Rename it. A job called
stuffcosts you the thing you wrote it for.
What you’ve learnt
- A subroutine is a piece of work with a name, written once and called wherever you need it.
DEFINEnames the job.CALLruns it, and the program carries on from where it called.- A shared job gives us one place to change behaviour that should stay the same.
- A name can also make a responsibility easier to understand and check.
What’s next
The divider does the same thing every time it runs. In Unit 3 a job takes a value and hands one back, which is what lets a single job do a whole family of things.