A3 postmortem — where the answer came from

Both of this week’s findings are the same mistake wearing two hats: the answer was read off the code instead of worked out from the rule.

Published

September 18, 2026

A3 is marked, and your own feedback is in your DMs. This is the other half: the things that came up in enough files to be worth writing once rather than sixteen times.

The code below is reconstructed — the shape several of you wrote, with the names and the numbers changed. Nobody’s file is quoted.

There is a theme, and it is worth naming before the list.

The answer came from the code instead of from the rule.

That sentence covers both of the findings below, which look like different problems and are not. One of them is about an if. The other is about a where: block. Both are what happens when the thing you consult to get an answer is the program you already wrote.

The boundary: if u < 10 means 10 is outlined

stripe from A2 opens with if u < 10, so a unit of exactly 10 misses that branch, falls through to the else, and is drawn outlined.

Four of sixteen wrote > 10, which drops exactly one number — and it is the number the assignment put in front of you twice by asking for a 10 in your examples. Writing the 10 down was never the hard part. Deciding what the answer at 10 was is the hard part, and it does not go away by having written it down.

The part worth sitting with

Two of those four got the same question right in Part C. There the page said, in bold, “between 9 and 18, including both” — and their show-ad includes both. Same student, same afternoon, >= in one problem and > in another.

The difference is not care and it is not ability. Part C told you where the edge was, in words. Part A made you read it off a function you already had.

Deriving a boundary from code in front of you is a different skill from applying one you were handed. It is the harder one, and it is the one worth practicing, because past this course nobody hands you the sentence in bold.

An example that cannot fail

This is the one to fix, and it turned up in four files in four disguises: an example stating the wrong answer because the body was consulted first, an example reporting that the empty list comes back holding something, an example written in terms of another function you also wrote.

Two of those disguises, reconstructed:

outlined([list: 4, 10, 25]) is [list: 25]
count-outlined([list: 4, 10, 25]) is 1 + how-many([list: 10, 25])

The first agrees with a body that tests u > 10, and the one number in dispute is the one it drops. Run that body, copy what came out, and this is the line you get — it will pass forever and it is wrong.

The second states its answer in terms of another function you also wrote. If both are wrong in the same way, and they usually are, the two mistakes cancel and the example still goes green.

An example that agrees with your code no matter what your code says is not a test. It is a second copy of the bug.

The where: block is worth what it is worth because it is the only part of the file that can disagree with you. It can only do that if the is side comes from the rule, by hand, before the body exists. What fixes this is the order, not the effort — several of you wrote a dozen examples and every one of them agreed with the same mistake.

The base case was fine

A3 called the empty branch the one mistake that mattered this week, and said so on the page.

Fourteen of fifteen wrote empty => 0.

That warning worked, or it was not needed. Either way the recursion is landing, which is the part people expect to be hard and it was not.

Problem 7 is the thing that falls off the end

Four of sixteen did not write it at all. It is last on the page, it is prose rather than code, and it is worth eight points — more than the boundary that took up most of this page.

The page said to answer it “in a comment,” which can mean a comment inside a3-lists.arr or the comment box on the submission itself. Both are fair readings, and both were marked the same way. If you used the comment box, that was not a mistake — it is also the right place to say what you did not finish.

Of the ones written, the good ones named a specific person and said what the program assumed about them. The thin ones restated the question.

Two that cost nothing this time

Neither of these lost anyone marks. Both are worth thirty seconds next time. We’ll start deducting points for these deviations eventually.

Three of sixteen gave a function a different name than the one asked for. The page invited it, so no points came off, and a name was not what that problem was measuring. Worth knowing anyway: the autograder calls a function by the name it was asked for or not at all. A name is part of the contract in the same way the types are.

Two files were not called a3-lists.arr. The grader looks for that exact name and finds nothing under any other, so on the first run those files scored zero until they were opened by hand. The file’s name is part of the contract too.

Where this goes next

The procedure all of this is measuring is on the design recipe page, and the step it keeps failing at is the same one it failed at on A1 and A2 — examples before the body, worked out from the rules and not from the code.

It can be the single change with the most marks behind it, and it costs you nothing but the order you do two things in.

The boundary half has somewhere to go too. Every question about an if is secretly a question about where its edges are, and the way to answer one is to pick the at least two numbers sitting near the edge and walk them through the branches in order.