Blog

Articles, tutorials, and insights about functional programming in Java

Subscribe via RSS
The Substitution Model: Evaluating Code in Your Head

The Substitution Model: Evaluating Code in Your Head

There is a reason you can verify algebra on a chalkboard but need a debugger for a service method: algebra lets you replace equals with equals, step by step, without losing the meaning. The substitution model is that same move applied to code — the mental evaluator that pure functional programs permit and side-effecting programs break. Here is how the technique works, where it stops working, and why your IDE's safest refactorings depend on it.

Project Valhalla and Value Classes: The Functional Payoff

Project Valhalla and Value Classes: The Functional Payoff

Functional Java has been writing checks that the JVM's object model charges a fee to cash: every Option, every Result, every small immutable record is typically a heap allocation carrying an identity nobody uses. Project Valhalla's value classes — previewing in JDK 28 — remove exactly that fee, and the code best positioned to collect is the code that was already immutable and identity-free. Here is what is actually coming, when, and why functional style is the natural beneficiary.

From Optional to Option: Migrating Null-Handling in an Existing Codebase

From Optional to Option: Migrating Null-Handling in an Existing Codebase

You do not migrate a codebase's null-handling with a big-bang rewrite — you migrate it the way flocks migrate: in formation, one leg at a time, everyone knowing the direction. Here is the mechanical playbook for moving a working codebase from Optional (and raw null) to Option: where to start, the conversion seams at every border, the API mapping, and the rules that keep a half-migrated codebase coherent while you finish.

Streams, Immutable Collections, and Efficient Data Processing

Streams, Immutable Collections, and Efficient Data Processing

The reflex objection to immutable data is cost: surely all that copying is slow. The JDK's answer is a pairing — lazy streams that fuse a whole pipeline into one pass, and immutable collections engineered so that sharing replaces copying. Here is how the two halves work together, where the real costs live, and when mutation is still the right tool inside a pure boundary.

Replacing if/else and switch Sprawl with Maps of Functions

Replacing if/else and switch Sprawl with Maps of Functions

A long if/else-if chain over the same discriminator is a lookup table wearing control flow as a disguise. Make the table explicit — a Map from key to function — and dispatch collapses to one line, handlers become values you can register, decorate, and test in isolation. Here is the pattern, the JDK mechanics, and the honest line where an exhaustive switch over a sealed type is the better tool.

Golden/Approval Tests for Functional Pipelines

Golden/Approval Tests for Functional Pipelines

When a pipeline's output is a large structured value, assert-by-assert testing collapses into either spot checks that miss regressions or assertion walls nobody maintains. Golden tests flip the deal: capture the whole output once, approve it, and let every future run diff against the approved version. Functional pipelines are the best possible customer for this technique — here is why, and how to do it honestly.

Coupling and Cohesion from a Functional Perspective

Coupling and Cohesion from a Functional Perspective

Coupling and cohesion predate objects — and the strongest form of cohesion in the original 1979 taxonomy is literally called functional. Seen through a functional lens, the two classic design forces stop being vague virtues: coupling becomes what your signatures hide, cohesion becomes what your types gather. Here is how pure functions, immutable data, and typed outcomes move code toward the good end of both scales.

Designing a Good Error Type: Sealed Hierarchies Callers Can Act On

Designing a Good Error Type: Sealed Hierarchies Callers Can Act On

Result<V, E> makes the failure part of the signature — but that only pays off if E is worth reading. A String is not an error type, and neither is a raw Throwable. The errors worth modeling are a sealed hierarchy whose cases are carved by what the caller can do about them. Here is how to design one.

Stream Gatherers: Custom Intermediate Operations in Modern Java

Stream Gatherers: Custom Intermediate Operations in Modern Java

For a decade the Stream API let you write your own terminal operation with Collector — but the middle of the pipeline was a closed shop of filter, map, and a handful of others. Gatherers, final since Java 24, open it up: a standard way to write stateful, short-circuiting, even concurrent intermediate operations. Here is how they work and when to reach for one.