Java-Friendly APIs

"There was no particular reason to respect the language of the Establishment."

– Norman Mailer, The Armies of the Night


Optics originated in Haskell, a language with rather different conventions to Java. Method names like view, over, and preview don't match what Java developers expect, and the parameter ordering (value before source) feels backwards to anyone accustomed to the receiver-first style.

This section addresses that gap with three complementary APIs, each suited to different needs.

Hands-On Learning

See Also


Which API Should I Use?

Start Here

For most users, the Focus DSL is the recommended starting point. It provides the most intuitive, IDE-friendly experience for navigating and modifying nested data structures.

┌─────────────────────────────────────────────────────────────────────────────┐
│                        CHOOSING YOUR API                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────┐                                                        │
│  │  Focus DSL      │ ◄─── START HERE                                        │
│  │  (Recommended)  │      Path-based navigation with full type safety       │
│  └────────┬────────┘      CompanyFocus.departments().employees().name()     │
│           │                                                                 │
│           │  Need validation-aware modifications?                           │
│           │  Working with Either/Maybe/Validated?                           │
│           ▼                                                                 │
│  ┌─────────────────┐                                                        │
│  │  Fluent API     │      Static methods + builders for effectful ops       │
│  │  (OpticOps)     │      OpticOps.modifyEither(user, lens, validator)      │
│  └─────────────────┘                                                        │
│                                                                             │
│  Need audit trails, dry-runs, or multiple execution strategies?             │
│  See [Advanced Optics](ch6_intro.md) for the Free Monad DSL.                │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

The Three APIs at a Glance

Focus DSL: The Primary API

The Focus DSL provides fluent, path-based navigation that mirrors how you think about your data:

// Navigate deeply nested structures with type safety
String city = CompanyFocus.headquarters().city().get(company);

// Modify values at any depth
Company updated = CompanyFocus.departments()
    .employees()
    .salary()
    .modifyAll(s -> s.multiply(1.10), company);

Use @GenerateFocus on your records to generate path builders automatically. The DSL handles collections (.each()), optionals (.some()), and indexed access (.at(index)) naturally.

Best for: Day-to-day optic operations, deep navigation, IDE discoverability, learning optics.

Fluent API: Validation and Effects

The OpticOps class provides static methods and builders for operations that involve validation or effects:

// Validation-aware modification with error accumulation
Validated<List<String>, Order> result = OpticOps.modifyAllValidated(
    order,
    orderPricesTraversal,
    price -> validatePrice(price)
);

// Effectful operations with type classes
Kind<CompletableFutureKind.Witness, User> asyncResult = OpticOps.modifyF(
    user, emailLens, this::fetchVerifiedEmail, cfApplicative
);

Best for: Validation pipelines, error accumulation, async operations, integration with Either/Maybe/Validated.

For programs-as-data, audit trails, and dry-runs, the Free Monad DSL lives in Advanced Optics.


In This Chapter

  • Focus DSL – The recommended starting point for most users, replacing manual lens composition with fluent, path-based navigation. The compiler tracks every step, so the IDE can autocomplete the full path through your data.
  • Optics for External Types – Generate optics for types you do not own (Jackson, JOOQ, Lombok, JDK classes) using @ImportOptics for shapes the processor can analyse directly. For trickier types, an interface that extends OpticsSpec<S> lets you declare the optics you want and tell the processor how to build them.
  • Kind Field Support – Records that contain a Kind<F, A> field are bridged into optics automatically. The processor reads cardinality semantics from your KindSemantics choice and generates the right path type, joining optics with the rest of the Higher-Kinded-J effect ecosystem.
  • Fluent API – When a modification can fail, accumulates errors, or runs asynchronously, the OpticOps class is the right tool. Static methods cover one-off cases; the fluent builders give you discoverable chaining for composing with Either, Maybe, Validated, and any other Applicative.

For Free Monad DSL and Interpreters, see the Advanced Optics chapter.


Chapter Contents

  1. Focus DSL - Path-based navigation with type safety and IDE support
  2. Optics for External Types - Generate optics for types you don't own
  3. Taming JSON with Jackson - Spec interfaces for complex external types
  4. Database Records with JOOQ - Copy strategies for builder-based types
  5. Focus DSL with External Libraries - Bridging Focus navigation into Immutables, Lombok, and beyond
  6. Kind Field Support - Automatic traversal for Kind<F, A> record fields
  7. Fluent API - Static methods and builders for validation-aware modifications

For Free Monad DSL and Interpreters, see Advanced Optics.


Next: Focus DSL