Fluent Calculator

Level: Advanced 60–90 min

Concepts: 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()
.Seed(10)
.Plus(5)
.Plus(5)
.Result(); // -> result = 20
int result = new Calculator()
.Seed(10)
.Plus(5)
.Minus(2)
.Undo()
.Undo() // -> 10
.Redo() // -> 15
.Result(); // -> result = 15

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.

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.