Word Count

A command-line tool in the spirit of wc: for each file given, it prints its lines, words and bytes, and then the ten most common words across all of them. It's the kind of small program many people write first, with files, arguments, text processing and error handling.

Files through the context

The program reads files with &fs.readText(path) and its arguments from &args - both context values. Normally they're the real file system and the real command line. The second test injects a MemoryFileSystem holding two made-up files, so it runs without touching the disk, and the third checks that a missing file is an IOError.

Counting words

#REGEX {{{ [\p{L}']+ }}} is a regular expression checked when the program compiles: runs of letters and apostrophes, so cat's is one word. matches finds every one. tally.update takes the word's current count - undefined the first time, hence n ?? 0 - and stores it plus one.

The tally's entries are (word, count) tuples. λ((word, n)) n takes one apart right in the lambda's parameter list, and let (word, n) := entry does the same in wordCount.

Cleaning up on errors

If a file can't be read, !! ends the program with the error. The defer before it is marked @onError, so it only runs when the loop's block exits with an error - here, to say which file failed. Other markers run a defer on a return, at a yield, or whenever the block ends.

Concepts shown

  • &args and &fs, with a MemoryFileSystem in tests.
  • Compile-time checked regular expressions with #REGEX.
  • Map.update, sorting, and destructuring tuples.
  • @onError defer for error-only cleanup.