A Fold
is an optic that can see into a structure and get 0 to N foci.
It is a generalization of fold
, and implements all operators that can be derived from it.
A structure S
that has a focus A
to which we can apply a function (A) -> R
with Monoid<R>
to S
and get R
.
For example, S == List<Int>
to which we apply (Int) -> String
with Monoid<String>
and we get R == String
Creating a Fold
can be done by manually defining foldMap
.
import arrow.core.*
import arrow.optics.*
import arrow.typeclasses.*
fun <T> nullableFold(): Fold<T?, T> = object : Fold<T?, T> {
override fun <R> foldMap(M: Monoid<R>, s: T?, f: (T) -> R): R =
s?.let(f) ?: M.empty()
}
Fold
has an API similar to kotlin.collections
, but because it’s defined in terms of foldMap
, there are no associative fold functions available.
nullableFold<Int>().isEmpty(null)
Fold.nonEmptyList<Int>().combineAll(Monoid.int(), nonEmptyListOf(1, 2, 3))
nullableFold<Int>().firstOrNull(null)
Fold.nonEmptyList<Int>().firstOrNull(nonEmptyListOf(1, 2, 3, 4))
Composing Fold
can be used for accessing foci in nested structures.
val nestedNelFold: Fold<NonEmptyList<NonEmptyList<Int>>, NonEmptyList<Int>> = Fold.nonEmptyList()
val nestedNel = nonEmptyListOf(1, 2, 3, 4).map {
nonEmptyListOf(it, it)
}
(nestedNelFold compose Fold.nonEmptyList()).getAll(nestedNel)
Fold
can be composed with all optics except Setter
, and results in the following optics.
Iso | Lens | Prism | Optional | Getter | Setter | Fold | Traversal | |
---|---|---|---|---|---|---|---|---|
Fold | Fold | Fold | Fold | Fold | Fold | X | Fold | Fold |
Do you like Arrow?
✖