Conversions

The methods that turn one optic type into another, and what each conversion gives up.

There are two ways an optic changes type in Higher-Kinded-J:

  1. You explicitly convert it by calling a method like asTraversal() or reverse().
  2. You compose it with another optic via andThen, and the result type is dictated by the Composition Rules.

This page covers (1). For (2), see the rules table.


Explicit conversion methods

MethodOnReturnsUse when
lens.asTraversal()Lens<S, A>Traversal<S, A>You need to call a traversal-only API (Traversals.modify, traversal andThen) on a lens.
lens.asFold()Lens<S, A>Fold<S, A>You want to express read-only intent or use fold-only operations like exists, find, all.
prism.asTraversal()Prism<S, A>Traversal<S, A>Same as for lenses: to call a Traversal-only utility such as Traversals.modify.
prism.asFold()Prism<S, A>Fold<S, A>Read-only query of a sum-type variant.
affine.asTraversal()Affine<S, A>Traversal<S, A>To call a Traversal-only utility such as Traversals.modify.
affine.asFold()Affine<S, A>Fold<S, A>Read-only access to the optional field.
iso.asLens()Iso<S, A>Lens<S, A>When you only need the forward direction; the reverseGet capability is dropped.
iso.asTraversal()Iso<S, A>Traversal<S, A>Same lifting as for a lens.
iso.asFold()Iso<S, A>Fold<S, A>Read-only access; reverseGet is dropped.
iso.reverse()Iso<S, A>Iso<A, S>Swaps the direction of the iso. The new optic's get is the old reverseGet and vice versa.
getter.asFold()Getter<S, A>Fold<S, A>Lifting a getter into a context that expects a fold.
setter.asTraversal()Setter<S, A>Traversal<S, A>Lifting a setter for use in traversal-shaped pipelines.
traversal.asFold()Traversal<S, A>Fold<S, A>Discarding write capability to express read-only intent or to call fold-only operations.

You cannot widen the other direction: a Traversal does not become a Lens, because it has no guarantee of focusing on exactly one element. The nearest thing is Traversals.partsOf, which gives you a Lens<S, List<A>> onto the focused elements as a list: a lens onto all of them, not onto one of them, which is precisely the guarantee a Traversal cannot make.


Implicit lifting during composition

andThen infers the result type automatically. Any two of Iso, Lens, Prism, Affine and Traversal compose directly, with no asTraversal() first; the composition rules table gives the result for each pair.

Lens<Order, List<LineItem>> linesLens = OrderLenses.lines();
Traversal<List<LineItem>, LineItem> listTraversal = Traversals.forList();

// No conversion required; the result is a Traversal because the rules say so.
Traversal<Order, LineItem> orderLines = linesLens.andThen(listTraversal);

You only need an explicit asTraversal() when the API you are calling requires a Traversal parameter and you have a Lens value not in a composition context, for example when storing the optic in a Traversal-typed field.


When to convert versus when to compose

SituationUse
Storing an optic in a typed fieldExplicit conversion (static final Traversal<...> X = lens.asTraversal();)
Calling a static utility (Traversals.modify, Fold query helpers)Explicit conversion
Chaining optics togetherComposition with andThen, no explicit conversion needed
Switching between read-only and read-write intentExplicit asFold()
Reversing the direction of an Isoreverse()

See Also


Previous: Optic Capabilities Next: Composition Rules