You're viewing the readable version of this site. The interactive extras (search, diagrams, read-aloud) need JavaScript and a current browser. Enable JavaScript; if it is already enabled, update your browser.

Learn · The Concrete Discrete Math of Real Systems

growing

Fold It Here or There; the Answer Stays Put

Why counting a pile of coins with a friend, merging intermediate totals, and MapReduce are the same trick — and the two small laws that decide whether your aggregation can be split across a thousand machines.

·

discrete-math, monoids, mapreduce, parallelism, aggregation, learn

MapReduce is a programming model and an associated implementation for processing and generating large data sets. Users specify a map function that processes a key/value pair to generate a set of intermediate key/value pairs, and a reduce function that merges all intermediate values associated with the same intermediate key.

Jeffrey Dean and Sanjay Ghemawat, "MapReduce: Simplified Data Processing on Large Clusters," OSDI, 2004

The preceding chapter found independent branches in a dependency graph. This chapter asks when one calculation may safely occupy them. You will learn the two laws that permit arbitrary splitting and regrouping; the repair for a summary that forgets too much; and why a hostile network demands two additional freedoms—order and repetition—before replicas can converge. The destination is not vocabulary. It is the ability to read a proposed merge function and predict its production failure.

A jar of coins and a friend

You and a friend face a big jar of coins. You pour it into two piles, count separately — you get $12, they get $15 — and add: $27.

Nobody checks the split was fair. Nobody recounts the whole jar. You know $27 is exactly what one careful person counting alone would have found. Three friends, ten friends, piles of wildly different sizes, subtotals added in any order — the answer cannot move.

Hold on to how unremarkable that feels, because the entire chapter is about why it's allowed to feel unremarkable — and about the operations where the same move, applied with the same confidence, silently gives the wrong answer.

The counterexample first: averaging breaks

Your class and another class each take the same test. Your class of 10 averages 90. The other class of 30 averages 70. The teacher, in a hurry, averages the two averages: (90+70)/2=80(90 + 70)/2 = 80.

The true average of all 40 students is (1090+3070)/40=75(10 \cdot 90 + 30 \cdot 70)/40 = 75. The hurried teacher is off by five points — not from a typo, but because average is not an operation you may regroup. Averaging two averages weights each group equally no matter how many students stand behind it.

Commit to a diagnosis before reading on.

Prediction checkpoint. What exactly went wrong with averaging the averages?

The fix is famous and it is the chapter's key move: don't pass along the average — pass along the pair (sum, count). Pairs add coordinate by coordinate: (900,10)(900, 10) and (2100,30)(2100, 30) merge to (3000,40)(3000, 40). Split them across any number of graders, merge in any grouping, and divide once at the very end: 3000/40=753000/40 = 75, exactly. The operation that failed the split gets repaired by carrying more state and postponing the lossy step to the finish line.

Naming the two laws

What does coin-adding have that raw averaging lacks? Two properties, each checkable in one line.

Grouping doesn't matter. Adding the first two piles before the third, or the last two before the first, lands on the same total. In words: when combining a row of things, where you put the parentheses is irrelevant. In symbols — and this is the law that does almost all the work in this chapter —

(ab)c=a(bc). (a \cdot b) \cdot c = a \cdot (b \cdot c).

Associativity — a two-input operation is associative when regrouping a chain of applications never changes the result. Addition and maximum are; subtraction and raw averaging are not. Learn more.

There's a do-nothing element. An empty pile contributes 00, and adding 00 changes nothing. The do-nothing element is what lets a worker who received no coins answer honestly instead of specially: every worker returns a number, and 0a=a0=a0 \cdot a = a \cdot 0 = a.

Identity element — a value ee that leaves every other value unchanged under the operation: ea=ae=ae \cdot a = a \cdot e = a. Zero for addition, one for multiplication, the empty string for concatenation. Learn more.

A set of values with an associative operation is a semigroup; add an identity element and it is a monoid. That's the entire definition — no more structure, no hidden clauses.

Associativity plus identity is the exact legal contract for "split the work anywhere, merge the parts in any grouping, and the answer cannot change."

Why exactly? Because any way of chunking a sequence and combining the chunks is just one way of parenthesizing the one long combination — and associativity says all parenthesizations agree. The identity handles the empty chunks. That one-paragraph argument, run in reverse, is also a diagnostic: if some chunking gives a different answer, the operation was not associative, full stop.

Think of freight on a train. It doesn't matter whether you couple cars one at a time from the front, or assemble three short trains in the yard and join them: the same cars end up in the same order carrying the same freight. Associativity is the yard rule that says "pre-assembling sections is always allowed." The identity is the empty section of track — coupling it on changes nothing, which is why a yard with no freight today doesn't need a special rule.

Watch a merge tree work — and stay lazy

Here is the coin count as a graph, in the exact shape you met in the DAG chapter: four piles at the left, two subtotal merges, one grand total at the right. This picture is what "split anywhere" looks like when you draw it.

Click a pile and watch what recomputes: its own path to the total — and nothing else. The other piles' subtotals are still valid, because the laws guarantee their values didn't depend on your pile. That is incremental aggregation: a monoid doesn't just let you parallelize a count, it lets you repair one without starting over.

The split-count-merge tree as a dependency graph. Click one pile and only its path to the total recomputes — the untouched subtotals are still perfectly good, which is why monoid aggregations can be both parallel and incremental. The tree shape is arbitrary: any regrouping computes the same total, and that freedom is exactly associativity.

Two chapters, one rhyme, worth saying out loud: the DAG says what depends on what; the monoid laws say the dependencies may be regrouped freely. Put together they give distributed computing its favorite picture — a merge tree where the shape is chosen for the hardware, not the mathematics, because the mathematics has promised not to care.

The monoid field guide

The laws are easy to check, and it's worth checking a handful you use weekly. Each of these is a monoid — combine any two, in any grouping, starting from the identity:

  • Sum of numbers; identity 00.
  • Maximum; identity -\infty (or "no reading yet"). Same for minimum with ++\infty.
  • Count; identity 00 — counting is just summing $1$s.
  • String or list concatenation; identity the empty string. (Order still matters — abbaab \ne ba — but grouping doesn't. Monoids don't require the operation to commute, and concatenation is the everyday proof.)
  • Set union; identity the empty set.
  • "Latest non-empty," "first non-empty"; identity "empty" — the merge rules inside many config systems.
  • Pairs and records of monoids, combined coordinate-wise — this is why (sum, count) worked: each coordinate is already lawful, so the pair is too. Composite monoids are how most real aggregations are built.

And the impostors, each failing one law:

  • Subtraction: (104)310(43)(10 - 4) - 3 \ne 10 - (4 - 3). Not associative.
  • Raw average: forgets the weight it needs to merge — repair with (sum, count).
  • "Longest run of consecutive errors" in a log: a run may straddle a chunk boundary, so per-chunk maxima under-count. Repairable, like average, by carrying more: each chunk reports (best run inside, run touching my left edge, run touching my right edge, chunk length), and those quadruples merge associatively. The pattern generalizes: many "not a monoid" verdicts are really "not yet — carry more state."
  • Floating-point addition, the honest asterisk: real float64 adds are only approximately associative — regrouping shifts rounding error. Systems that shard float sums accept a tolerance or use compensated/fixed-point representations. The law you rely on is exactly as strong as the arithmetic underneath it.
Prediction checkpoint. Commit before reading the field-guide entry again.

Now stop taking my word for any of this. The machine below holds the whole field guide; pick an operation and it exhaustively checks every law over that operation's sample values — certifying the contract or handing you the exact regrouping that breaks it. Watch average fail with a concrete triple, then watch the (sum, count) repair pass.

The law checker, live. Pick an operation; the machine checks associativity, commutativity, idempotence, and identity over its sample domain and answers the only question that matters here: is this safe to split across workers? Failures come with the exact counterexample.

One more law, one more superpower

The jar-counting story quietly used a third idea, and naming it pays. When you count a pile, you turn coins (a list) into a number (the running total), and the rule "count of two piles poured together = sum of the two counts" is what made the split legal. A translation between two worlds that respects their combining operations —

f(ab)=f(a)f(b) f(a \, \square \, b) = f(a) \cdot f(b)

— is a homomorphism: measure the pieces, or measure the whole, and the books agree. Every "summarize each shard, then merge summaries" architecture is an appeal to exactly this equation: the summary function must be a homomorphism from "concatenate the raw data" to "merge the summaries," or the shortcut lies. The (sum, count) repair made averaging homomorphic; the edge-runs repair did the same for the longest-run job.

Homomorphism — a structure-respecting map: combining then measuring equals measuring then combining. Learn more.

A network buys two more freedoms

The scheduler in the coin story controls every input exactly once. A network controls much less. It may deliver Alice's update before Bob's on one replica and after it on another. A retry may deliver Alice's update twice. Associativity forgives regrouping, but neither accident.

Use a grocery set as state and merge by union. Three equations describe exactly what the network is allowed to get wrong:

  • Associativity: (ab)c=a(bc)(a \cup b) \cup c = a \cup (b \cup c) forgives regrouping.
  • Commutativity: ab=baa \cup b = b \cup a forgives reordering.
  • Idempotence: aa=aa \cup a = a forgives duplication.

Together they form a join-semilattice merge. Order states by inclusion: aba \le b means that bb contains at least the information in aa. Union is the smallest state above both inputs—their join. It loses nothing either side knew and invents nothing else.

Join-semilattice — a partially ordered set in which every pair has a least upper bound, written aba \sqcup b. Its join is automatically associative, commutative, and idempotent. See Davey and Priestley, Introduction to Lattices and Order.

Grouping-freedom, order-freedom, and repeat-freedom are separate purchases. A monoid buys the first; convergent replication needs all three.

The distinction catches a famous trap. Integer addition is associative and commutative but not idempotent: 1+111+1 \ne 1. Replaying a +1 message after a timeout silently overcounts. A grow-only replicated counter repairs the state instead: each replica owns one component, increments only its own component, and replicas merge componentwise by maximum. Maximum is idempotent. The visible total is the sum of the components after merging.

Prediction — identify the network freedom before choosing the law.

This is the algebra behind conflict-free replicated data types. The original CRDT formalization by Marc Shapiro, Nuno Preguiça, Carlos Baquero, and Marek Zawirski separates state-based objects, whose states merge by join, from operation-based objects, whose concurrent operations commute. The engineering differs; the demand for lawful freedom is the same.

The same laws, three famous times

MapReduce and its descendants. The epigraph is the contract read out loud: map turns each record into a measurable piece; reduce "merges all intermediate values associated with the same intermediate key." The original paper is explicit that the machinery — partitioning, scheduling, re-execution on failure — belongs to the runtime, and re-execution is only safe because merging the same pieces again, or in a different grouping, cannot change a lawful answer. Spark and its reduce/fold/aggregate operations inherit the same requirement, and its documentation warns that the operation passed in must be associative for exactly this reason.

Streaming and mergeable metrics. A metrics pipeline that pre-computes per-minute (sum, count, min, max) records can roll them up into hours and days by pure merging — a composite monoid doing its coordinate-wise thing. The same idea powers approximate "mergeable summaries" (HyperLogLog for distinct counts, quantile sketches): each shard keeps a small sketch, and sketches merge associatively. A trip through those structures is on this tour's backlog; the lawful merge is the family membership card.

Event sourcing and the incremental fold. An event-sourced system stores an append-only log and derives state by folding the events. Concatenation of logs is a monoid; when the state summary itself also merges lawfully, snapshots and partial folds can be cached, merged, and repaired incrementally — the merge tree you clicked above, running continuously in production.

Where the laws run out

  • Not everything regroups. Some jobs genuinely need the whole sequence in order — running a state machine over a log, for instance, where each step depends on the state so far. (Some of those still parallelize by a clever monoid over functions, but not all, and never for free.)
  • Commutativity and idempotence are separate purchases. Monoids promise nothing about reordering or duplication—concatenation and addition are the counterexamples. Replicas pay for those freedoms with stronger laws.
  • Floating-point, again. The asterisk is real: regrouped float sums drift. Decide the tolerance before sharding, not after the audit.
  • The laws must be checked, not assumed. A custom merge(a, b) written in an afternoon is one unchecked associativity failure away from answers that depend on cluster size. Property-based tests — throw random triples at (ab)c=a(bc)(a \cdot b) \cdot c = a \cdot (b \cdot c) — catch these cheaply, and are among the highest-value tests per line ever written.

Lessons

  • An aggregation may be split across workers and merged in any grouping exactly when its operation is associative with an identity — a monoid. The proof is one sentence: every chunking is a parenthesization.
  • The classic failure is an intermediate summary that forgot information its merge needs (average without count); the classic repair is to carry more state and postpone the lossy step.
  • Composite monoids (pairs, records, coordinate-wise merge) are how real aggregations are engineered lawful.
  • A shard-then-summarize architecture is an appeal to a homomorphism; if measuring pieces disagrees with measuring the whole, the shortcut lies.
  • Grouping-freedom (associativity), order-freedom (commutativity), and repeat-freedom (idempotence) are three separate purchases; monoids buy only the first.

Practice

Retrieval — the contract, from memory.
Discrimination — which repair is which.
Transfer — a system this chapter never mentioned.

What happens when part of the input is fixed?

So far the summaries became smaller while the operation stayed the same. Another kind of shrinkage happens when the operation itself learns one fact. A search over every possible word becomes a smaller search after the reader types c. A regular expression becomes a new regular expression after it consumes one character. A program with a known configuration becomes a simpler program before it runs.

The next chapter names this residual: the problem that remains after one piece of its input has already been paid for.

References

  1. Dean & Ghemawat. “MapReduce: Simplified Data Processing on Large Clusters.” OSDI, 2004. — the epigraph's source — §2 and §4.3 are the merge contract in the system's own words; twenty minutes, well spent
  2. Monoid.” — the reference definition, plus the homomorphism section this chapter leans on
  3. RDD Programming Guide: reduce, fold, aggregate.” Apache Spark docs. — the associativity requirement, live in a production API you may already use
  4. Agarwal, Cormode, Huang, Phillips, Wei, Yi. “Mergeable Summaries.” PODS, 2012. — the paper that made 'summaries that merge lawfully' a first-class research object — HyperLogLog and quantile sketches live downstream of it
  5. Flajolet, Fusy, Gandouet, Meunier. “HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm.” AofA, 2007. — distinct-counting as a mergeable sketch — read after this chapter's laws feel comfortable
  6. Graham, Knuth & Patashnik. “Concrete Mathematics.” Addison-Wesley, 1994. — ch. 2 (sums) is the classical muscle behind everything summed here
  7. Okasaki. “Purely Functional Data Structures.” Cambridge, 1998. — for the reader who wants to see monoid-shaped thinking build entire libraries of structures
  8. Shapiro, Preguiça, Baquero, Zawirski. “Conflict-free Replicated Data Types.” SSS, 2011. — the primary formalization of state-based join and operation-based commutativity