More Functional Thinking
These companion blog series chart a path from foundational type theory to production-ready optics in Java. Start with the Foundations if you're new to functional programming in Java, or jump straight to the Functional Optics series to see Higher-Kinded-J in action.
Functional Optics for Modern Java NEW — 6 Part Series
Java records and sealed interfaces make immutable data modelling elegant — but updating deeply nested immutable structures still means tedious copy-constructor cascades. This series closes that gap. Across six posts you'll move from the problem, through the theory, and into a fully working production pipeline built with Higher-Kinded-J.
Part 1 — The Immutability Gap: Why Java Records Need Optics
Pattern matching solves the read side beautifully — but what about writes? This opening post reveals how operations that should be one-liners balloon into 25+ lines of manual reconstruction, and introduces optics as the composable answer.
"Pattern matching is half the puzzle; optics complete it."
Part 2 — Optics Fundamentals: Lenses, Prisms, and Traversals
Meet the three core optic types: Lenses for product-type fields, Prisms for sum-type variants, and Traversals for collections. Learn the lens laws, see how @GenerateLenses and @GeneratePrisms eliminate boilerplate, and discover how small, focused optics compose into powerful navigation paths.
Part 3 — Optics in Practice: An Expression Language AST
Theory meets code. Build a complete expression language using sealed interfaces and records, then apply lenses, prisms, and the Focus DSL to implement constant folding and identity simplification — all without hand-written recursion.
Part 4 — The Focus DSL: Traversals and Pattern Rewrites
Scale up from single nodes to entire trees. TraversalPath and bottom-up/top-down strategies handle recursive descent, while modifyWhen and foldMap enable filtered updates and aggregation. A multi-pass optimisation pipeline brings constant folding, dead-branch elimination, and common-subexpression detection together.
Part 5 — The Effect Path API: Railway-Style Error Handling
Introduce effects into optics. MaybePath, EitherPath, ValidationPath, and VTaskPath let the same traversal code work across different computational contexts — fail-fast for quick feedback, accumulating for comprehensive validation, and concurrent via virtual threads.
Part 6 — From Theory to Practice
The capstone. Wire Focus DSL + Effect Paths into a four-phase expression pipeline, integrate with Spring Boot via hkj-spring-boot-starter, generate optics for third-party types with @ImportOptics, and map out a pragmatic incremental migration path for real teams.
All six parts have runnable examples in the expression-language-example repository. Clone it and follow along.
Foundations: Types and Functional Patterns
This earlier series explores the foundational ideas that inspired Higher-Kinded-J's development. Each post builds the theoretical knowledge that underpins the optics series above.
-
Algebraic Data Types and Pattern Matching with Java — How ADTs and pattern matching model complex domains and improve on the traditional Visitor Pattern.
-
Variance in Generics, Phantom and Existential Types with Java and Scala — Use-site vs declaration-site variance, erasure trade-offs, and how phantom and existential types extend the type system.
-
Intersection and Union Types with Java and Scala — Modelling tighter constraints with intersection types and increasing flexibility with union types.
-
Functors and Monads with Java and Scala — Cleaner, more composable code for null handling, error management, and asynchronous sequencing.
-
Higher Kinded Types with Java and Scala — How higher-kinded types reduce duplication and unlock flexible, generic abstractions.
-
Recursion, Thunks and Trampolines with Java and Scala — Converting deep stack-based recursion into safe, heap-based iteration.