liveBook · Manning

Imperative i/o

As explained in Functional Programming in Scala, Second Edition

Imperative I/O is an input/output style that combines control flow, algorithmic logic, and resource management through explicit stateful operations.

Imperative I/O performs an algorithm through explicit control flow, mutable state, iteration, and resource operations. It can be incremental and terminate early, but it mixes the algorithm with low-level resource-safety details. section 15.1 section 15.6

Counting lines without loading the file

A program checking whether a file exceeds 40,000 lines opens a source, obtains an iterator, increments a mutable counter inside a while loop, stops once the answer is known, and closes the source in finally. The approach keeps memory use low and supports early termination, but an omitted or misplaced close can leak a scarce file handle. section 15.1

Listing 15.1 Counting line numbers in imperative style
def linesGt40k(filename: String): IO[Boolean] = IO:
  val src = io.Source.fromFile(filename)             #1
  try
    var count = 0
    val lines: Iterator[String] = src.getLines()     #2
    while count <= 40000 && lines.hasNext do
      lines.next                                     #3
      count += 1
    count > 40000
  finally src.close

Use the listing to inspect how mutable state, incremental iteration, early termination, and finally-based cleanup combine in one IO action. section 15.1

Imperative I/O exposes several recurring tensions:

  • Incremental processing avoids loading an entire file, while mutable iterators and loops entangle that behavior with the algorithm. section 15.1
  • Resource safety requires closing files after normal completion or exceptions and avoiding reads after closure. section 15.1
  • Streaming abstractions separate composable computation from these operational details and can preserve early termination, error handling, and effect evaluation. section 15.6

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Explains a concrete implementation and its resource leak risk, then contrasts it with composable streaming abstractions.
Examples
Demonstrates counting whether a file exceeds 40,000 lines with a while loop, mutable iterator, counter, and finally block.
What it assumes
Assumes familiarity with IO actions, file iteration, mutable variables, exceptions, and resource finalization.
What you can do after
Evaluate imperative implementations for incremental behavior, early termination, resource leaks, and separation from composable streaming designs.
How it connects
Connects imperative I/O to the IO monad, resource safety, streaming computations, Stream, Pull, and early termination.

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.