Optic Capabilities

Which operations work on which optic

What You'll Learn

  • The cardinality of focus for each optic type (exactly one, zero or one, zero or more, none).
  • Which read, write, modify, query, and effectful operations each optic supports.
  • Which type conversion methods are available on each optic.
  • Where collection-shaped operations live (some on optics, some on the Traversals and Fold utilities).

This is the lookup table for "can a Prism do getAll? does a Getter have set?" The narrative pages explain why each optic has the capabilities it does; this page just lists them.


Cardinality at a glance

OpticFocus cardinalityReads?Writes?
Lens<S, A>exactly oneyesyes
Iso<S, A>exactly one (reversible)yesyes
Prism<S, A>zero or oneyesyes (and can construct)
Affine<S, A>zero or oneyesyes
Traversal<S, A>zero or moreyesyes
Fold<S, A>zero or moreyesno
Getter<S, A>exactly oneyesno
Setter<S, A>zero or morenoyes

Method support

A means the interface declares the method, so you can call it directly on that optic. A blank cell means you cannot: either the operation makes no sense for that optic, or it is reachable only after a conversion. A note in the cell points at the utility class or conversion that gets you there.

That distinction matters, because most optics reach most operations eventually. A Lens has no getAll, but lens.asFold().getAll(source) works; a Traversal has no modify, but Traversals.modify(traversal, f, source) does. The table below is about what is on the type; Conversions is about how to get from one type to another.

One deliberate exception: Fold declares its own read-only modifyF, which runs the effects and returns the input unchanged. Getter extends Fold and inherits exactly that, so both are marked rather than . Setter inherits modifyF as an abstract obligation from Optic, so every concrete Setter implements it for real.

MethodLensIsoPrismAffineTraversalFoldGetterSetter
get(S) → A
getOptional(S) → Optional<A>via previewvia preview
getAll(S) → List<A>via asFold()via asFold()via asFold()via asFold()via asFold() or Traversals
preview(S) → Optional<A>via asFold()via asFold()via asFold()via asFold()via asFold()
matches(S) → boolean
set(A, S) → Svia Traversals.modify(t, a -> v, s)
modify(f, S) → Svia Traversals
modifyF(f, S, App) → Kind<F, S>✗ (see note)✗ (see note)
build(A) → S✓ (reverseGet)
foldMap(monoid, f, S) → Mvia asFold()via asFold()via asFold()via asFold()via asFold()
exists, all, find, isEmpty, lengthvia asFold()via asFold()via asFold()via asFold()via asFold()

Two rows are worth reading twice. Iso carries only get, reverseGet and modifyF: it has no set and no modify of its own, because reverseGet already rebuilds the whole structure from the focus. And Getter extends Fold, so it inherits the entire query family, getAll and preview included; every other read-only capability in the Fold column applies to Getter too.


Collection helpers (Traversals utility)

Bulk operations on Traversal values typically live on the Traversals utility class rather than the Traversal interface itself. The same applies to a handful of factory methods.

MethodPurpose
Traversals.modify(t, f, S)Apply f to every focused element
Traversals.getAll(t, S)Collect every focused element into a List
Traversals.filtered(predicate)A traversal that focuses only on matching elements
Traversals.forList()Standard traversal over List<A> elements
Traversals.forSet()Standard traversal over Set<A> elements
Traversals.forCollection()Traversal over Collection<A> elements; a set source is rebuilt as a set, anything else as a List
Traversals.forMap(key)Traversal focused on the value at key in a Map
Traversals.forMapValues()Traversal over every value in a Map
Traversals.forOptional()Traversal that focuses zero or one elements of an Optional
Traversals.forArray()Traversal over the elements of an array

Stay in the static-method utility for one-off bulk operations; reach for the Fluent API when you want method-chaining on a builder.


Conversions and composition

All optic types expose andThen(other) for composition; the result type follows the rules in Composition Rules. The conversion methods between optic types are catalogued in Conversions.

Key Takeaways

  • A means the method is on the type. A cell naming a conversion means you can still get there, one asFold(), asTraversal() or Traversals call later. An empty cell means only that the type does not declare it. Sometimes the operation is meaningless there — a Setter has no read at all, and matches never fails on an optic that always hits — and sometimes a conversion still reaches it, as iso.asLens().set(...) does.
  • Iso is smaller than it looks. get, reverseGet and modifyF: no set and no modify of its own, because reverseGet already rebuilds the whole structure. asLens() is the route when you want them.
  • Traversal declares no plain read or write. It has modifyF (plus filtered, filterBy, branch and modifyWhen); every get, set and modify goes through Traversals or asFold().
  • Getter extends Fold, so it inherits the whole query family: getAll, preview, exists, all, find, isEmpty and length.
  • Setter is zero-or-more and write-only. It has set and modify and no way to read at all.

See Also


Previous: Reference Next: Conversions