What goes wrong when the seed of a till fold is not an identity for its combining step?
answer
- the value that changes nothing
- zero adds, one multiplies
- a property of the pair, not the value
- an empty day returns the seed
- split the work and the seed repeats
basics
~20 sThe seed's own value is baked into every result. An empty day reports the seed instead of nothing, and if the same fold is later run per chunk the seed is injected once per chunk instead of once overall.
solid answer
~50 sAn identity for a combining step is the value that changes nothing: combining it with any value gives that value back. Zero for addition, one for multiplication, the empty collection for appending, the empty totals record for posting a sale. Seed the fold with the identity and the seed contributes nothing of its own, so the result is purely what the day's transactions say, and an empty day correctly reports nothing. Seed it with anything else - yesterday's closing float, a "safe" zero for a product fold - and that value is part of the answer forever after. Two consequences bite in practice: the empty-input result is the wrong number rather than a neutral one, and the moment the fold is split into chunks each chunk applies the seed again, so a seed of 100 across four chunks adds 400.
code
pseudocode · 10 lines// Identity for adding is 0 - the seed contributes nothing
fold([12, 8, 5], 0, add) // 25
// Same step, non-identity seed - 100 is in the answer
fold([12, 8, 5], 100, add) // 125
fold([], 100, add) // 100 on an empty day
// Identity for multiplying is 1, not 0
fold([3, 4, 2], 1, multiply) // 24
fold([3, 4, 2], 0, multiply) // 0 - every product destroyedgo deeper
Know that the seed is the accumulator's starting value and is returned for an empty sequence, and that zero belongs with addition while one belongs with multiplication.
Define an identity as a property of a value and a step together - combining it with anything returns that thing - and show what a non-identity seed does to the empty-day result.
Connect the seed to operations: a non-identity seed is applied once per fold started, so chunking a job silently multiplies it, and that is the defect that appears only after the work is parallelised.
Set the convention: folds start from the identity and any genuine opening balance is applied once outside the fold, so that jobs stay safe to split without re-auditing every seed.
## What "identity" means here A combining step and a value form a pair. The value is an **identity element** for that step when combining it with anything returns that thing unchanged, from either side: - `combine(identity, x)` is `x` - `combine(x, identity)` is `x` It is a property of the *pair*, never of the value alone. Zero is the identity of addition and annihilates multiplication; one is the identity of multiplication and skews addition. So "which seed?" is answered by first asking "which combining step?". | Combining step | Identity | A seed that is not the identity | |---|---|---| | Add amounts | `0` | any float or opening balance | | Multiply quantities | `1` | `0`, which forces the result to zero | | Append to a collection | empty collection | a collection with a placeholder row | | Keep the larger | the smallest representable value, or absent | the first sale you happen to have | | Post a sale into totals | the empty totals record | last night's closing record | ## The three failures a non-identity seed produces 1. **The empty case reports the seed.** A fold over a day with no transactions returns its seed untouched. If that seed is last night's closing record, a closed shop reports a full day's takings. 2. **Every result carries the seed's contribution.** Not a rounding error - a systematic offset present in every run, which is exactly the kind of defect that survives testing because the test fixture uses the same wrong seed. 3. **Splitting the work multiplies it.** Run the same fold over four chunks and merge the partials and the seed has been applied four times. Seed `100`, four chunks, and the total is `400` too high. The seed is the only part of a fold whose contribution depends on how many times the fold is *started*, and the identity is precisely the value for which "how many times" stops mattering. ## The annihilator trap The most common concrete version is seeding a multiplying fold with `0`. Zero looks like the safe neutral starting point because it is the neutral starting point for the operation people fold with most, and the result is not merely skewed but destroyed: every product collapses to zero regardless of the data. It is worth naming the two roles separately - **identity** leaves a value alone, an **annihilator** replaces it - because the two are often the same two constants swapped. An identity is also not a *fixed point*. A fixed point of a step is a value the step maps to itself for one particular partner; an identity leaves **every** partner unchanged. A candidate who says "the identity is the value the step maps to itself" has stated the weaker property. ## When a non-identity seed is the right call This is a design decision rather than a bug when it is deliberate. A reconciliation that genuinely must start from an opening float has a real starting value, and there is nothing wrong with folding from it - provided you accept two obligations: - the fold must be started **exactly once**, which rules out naively chunking it; - the empty-day result is now "the float", which has to be the intended answer. The cleaner arrangement is to fold the transactions from the identity and add the float once at the end. The fold then stays neutral and splittable, and the one non-neutral contribution is visible in a single place rather than hidden in a parameter. ## Identity and the empty structure are the same question There is a neat way to hold this: **whatever a fold returns for an empty structure is whatever the seed is**, so choosing the seed *is* answering "what should an empty day report?". Ask that question first and the identity usually names itself. Nothing sold means zero takings, no rows means an empty collection, no measurements means no maximum - which is why a maximum fold over an empty input has no honest number to return at all, and either uses the smallest representable value as its identity or declares the result absent. ## What an interviewer is checking They want to see that you treat the seed as a contribution to the answer rather than as boilerplate. A candidate who says "I just start it at zero" has a model in which the seed is inert. It is not: it is one more operand, present in every run, and the only way to make it inert is to pick the value the step cannot see.
- What is the identity for a fold that keeps the largest transaction?Either the smallest representable value of the amount type, or an explicit "absent" accumulator that the step replaces on first sight of an element. The second is usually better: an empty day genuinely has no largest sale, and the smallest representable value is a real number that downstream code will happily print.
- The reconciliation really does start from an opening float - how do you keep the fold neutral?Fold the transactions from the identity, then add the float once to the finished result. The fold stays splittable and its empty case stays honest, and the single non-neutral contribution sits in one visible line instead of a parameter.
saying these in an interview costs you the question
- Says any convenient starting value will do
- Seeds a multiplying fold with zero
- Treats the empty case as something the fold handles itself
- Assumes a non-identity seed is applied once however the fold runs
- Defines the identity as the smallest value present
- Calls a value an identity without naming the combining step