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:
- You explicitly convert it by calling a method like
asTraversal()orreverse(). - 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
| Method | On | Returns | Use 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
| Situation | Use |
|---|---|
| Storing an optic in a typed field | Explicit conversion (static final Traversal<...> X = lens.asTraversal();) |
Calling a static utility (Traversals.modify, Fold query helpers) | Explicit conversion |
| Chaining optics together | Composition with andThen, no explicit conversion needed |
| Switching between read-only and read-write intent | Explicit asFold() |
Reversing the direction of an Iso | reverse() |
- Composition Rules: the rules table for what type results from
andThen - Optic Capabilities: which methods are available on each optic
- Decision Trees: choosing the optic before you convert it
Previous: Optic Capabilities Next: Composition Rules