Lesson 01 · Functional Programming in Scala

What Is a Pure Function?

Make a standalone synthetic telemetry calculation predictable.

Tangible win

You will write a telemetry calculation that can be tested, reused, and reasoned about without running a database, clock, network, or dashboard. All readings are synthetic and included in the browser lab: no VPS, account, private repository, or production service is required.

Retrieve before reading

In one sentence: what should determine the result of a function that calculates water usage?

Reveal a strong answer

The result should be determined only by the function's explicit inputs. Nothing else should secretly change it.

The core idea

A pure function has two properties:

  1. For the same inputs, it returns the same output.
  2. Calling it has no observable side effect.

That gives us referential transparency: if an expression produces a value, we can replace the expression with that value without changing the program's behavior.

Flow Insight

usageFor("M-01", readings) is a good pure-function boundary. The meter id and readings are visible inputs; the result does not depend on the current time, a file, or a hidden dashboard session.

Scala shape

final case class Reading(meterId: String, liters: Double)

def usageFor(meterId: String, readings: List[Reading]): Double =
  readings
    .filter(_.meterId == meterId)
    .map(_.liters)
    .sum

The function reads data and returns a number. It does not mutate the list, write a report, or print a message. The same list and meter id always produce the same answer.

Pure core, effect boundary

Functional programs do not avoid all effects. They push effects to an explicit boundary and keep the important reasoning in pure functions.

// Pure core
val liters = usageFor("M-01", readings)

// Effect boundary
println(s"M-01 usage: $liters liters")

The calculation is easy to test. The printing is still useful, but it is visible as an effect at the edge of the program.

Rust bridge

This is close to a Rust function over a slice or iterator: explicit arguments in, a value out. Scala's case class is the small data record; map, filter, and sum compose the transformation.

Interactive coding checkpoint

Use the browser lab to change visible inputs, run the calculation, and watch the result change only when the inputs change.

Browser lab / pure core

Transform readings into a result

Total usage 15.0 liters
M-01 usage 12.0 liters

Same inputs. Same result.

Try this sequence:

  1. Run the function twice without changing anything. The output stays the same.
  2. Change one explicit input, such as 4.5 to 8.5, then run again.
  3. Add a reading for a new meter and select it. No hidden server state is involved.
Optional Scala bridge

The browser exercise is the main path. If you later want to compare syntax, the Scala source file contains the same fixture data and assertions. You do not need a CLI to complete this lesson.

Quick check

Which definition is pure?

Spaced review

Before the next lesson, answer these from memory:

Keep the compact version nearby: Pure functions reference.

Primary source: Chiusano and Bjarnason, Functional Programming in Scala, Chapter 1. For runnable companion material, see the official first-edition exercise repository. This page is an original, self-contained lesson; the book PDF is not required to run the checkpoint.