liveBook · Manning

Word count

As explained in Functional Programming in Scala, Second Edition

The number of whitespace-delimited words represented by a character sequence or word-count summary.

Word count seems like an integer reduction until input is split at an arbitrary character boundary. Splitting "lorem ipsum dolor sit amet, " into "lorem ipsum do" and "lor sit amet, " must count dolor once, not zero or twice. An integer loses the unfinished fragments needed to combine those results correctly. section 10.4

Represent an intermediate result as either `Stub(chars)`, containing no confirmed whole words, or `Part(lStub, words, rStub)`, containing a confirmed count plus unfinished left and right fragments. Thus `"lorem ipsum do"` becomes `Part("lorem", 1, "do")`: only `ipsum` is certainly complete. section 10.4

enum WC:
  case Stub(chars: String)
  case Part(lStub: String, words: Int, rStub: String)

`WC` preserves precisely the boundary information that a plain `Int` discards. section 10.4

The combining operation preserves character order. Two stubs concatenate. Combining a stub and a part extends the part’s outer fragment. Combining two parts adds their confirmed counts and counts the joined inner fragments as one word when that joined string is nonempty. This makes independently reduced substrings composable. section 10.9

A correct empty value and reduction complete the count:

  • Use `Stub("")` as the monoid identity. Using `Part("", 0, "")` fails identity because combining it with a stub changes the result’s representation. section 10.9
  • Map each non-whitespace character to a `Stub`; map whitespace to an empty `Part`, so whitespace establishes a word boundary. section 10.9
  • Fold an `IndexedSeq[Char]` with `foldMapV`, then count nonempty outer stubs along with the confirmed `words` field. section 10.9
  • Check associativity and identity with generated `WC` values before trusting the implementation. section 10.9

Counting across a split word

The left substring produces `Part("lorem", 1, "do")`; the right produces `Part("lor", 2, "")`. Combining them joins `do` and `lor`, adds one completed word, and retains the outer stubs. The final result includes lorem, ipsum, dolor, sit, and amet. section 10.4 section 10.9

The central design move is to make uncertainty explicit. A monoid result need not be the final answer at every stage; it can carry enough local context for associative combination, then be interpreted as an `Int` only after the complete input has been folded. section 10.4 section 10.9

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Derives a `WC` algebraic data type, all four combine cases, law tests, and a character-folding implementation.
Examples
Works through splitting "lorem ipsum dolor sit amet, " inside "dolor" and combines the resulting `Part` values.
What you can do after
Build it from scratch by defining a lawful `WC` monoid and folding whitespace-classified characters into a final count.
How it connects
Connects word count to monoids, associativity, identity, algebraic data types, `foldMapV`, and property-based testing.

Also explained in

Compare how 2 books explain word count →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.