Fluent Calculator
Level: Advanced 60–90 minConcepts: Algorithms
Solutions: C# | TypeScript | Python
Create a calculator class that uses fluent syntax to perform addition and subtraction. It also supports undo and redo as part of the fluent syntax.
- All input values should be int.
- The calculator should never throw exceptions.
- After the first call to Seed additional calls are ignored.
Examples
| Examples |
|---|
int result = new Calculator()
|
int result = new Calculator()
|
Hint
To avoid booleans when checking seed’s candidacy to be called, use method chaining with Progressive Interfaces. E.g Return an interface with which methods can be chained together instead of the Builder Object with all methods on it.
Bonus
Add a save method which when called saves the state of the previous operations. Undo or Redo operations called after a save have no effect.
int result = new Calculator()
.Seed(10)
.Plus(5)
.Minus(2)
.Save() // -> 13
.Undo()
.Redo()
.Undo()
.Plus(5)
.Result(); // -> result = 18
Reference Walkthrough
Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/fluent-calc. Fifteen scenarios cover the full surface — seeding (first-call-wins), Plus/Minus, Undo/Redo with classic redo-stack invalidation on a new operation, and the Save bonus that clears history so subsequent Undo/Redo calls have no effect. Every language mirrors the spec’s “never throws” rule: unseeded operations and empty-stack Undo/Redo return the chain untouched.
- C# (.NET 8, xUnit, FluentAssertions 6.12.0) — walkthrough
- TypeScript (Node 20, Vitest 1.6, TS 5 strict) — walkthrough
- Python (3.11, pytest) — walkthrough
This kata ships in Agent Full-Bake mode at high gear. It bends the F1 rule slightly — a Calculator is a stateful object with identity, not a pure input→output function — but the F1 discipline still holds: no test data builders, because the fluent chain itself is the builder. Tests construct scenarios by calling the calculator’s own methods; the fluent API is the SUT. Each implementation is mutable (chained methods return the same instance) — an immutable variant would allocate a new object and two new stacks per chain step for no teaching benefit. The hint’s progressive-interfaces technique (returning a narrower interface after Seed so repeat-Seed becomes a compile error) was considered and rejected for the reference: at F1’s one-screen scope it doubles the file size to catch what a single runtime test already covers. See the repo’s Gears section for when high gear is the right call.
Related reading
- Katas Are Rehearsal, Not Performance
You don't practice TDD on production code. You practice on katas and bring the muscle memory to production. The gap between knowing TDD and doing TDD is reps. - The Test Pyramid Was an Economic Argument
The test pyramid was not a quality law. It was a cost structure: unit tests were cheap, integration tests were expensive, so you wrote many of the first and few of the second. Agents collapsed the cost of writing tests at every level, and the cheapest test that still tells the truth is the one that pins a seam the agent cannot fake. - Agents Pattern-Match Your Test Smells
Test smells used to drift in at typing speed. Agents reproduce them at generation speed because they sample whatever is already in the suite. The smells in your codebase are now the smells in your roadmap.