Collectors
Collectors help build complex computations over sequences of values, guaranteeing that those values are consumed only once.
Take for example the computation of the average of a list. You can certainly write a simple version using the built-in functions,
val average = list.sum() / list.size
Note however that this implementation traverses the list twice,
one per operation over the list. This may not be a problem for small
lists but could become more problematic with longer collections.
Some data structures, like Sequence
or Flow
, impose an
even larger footprint, as their elements are computed every time
you need a new one.
Collectors separate the description of the aggregation you want
to perform from the actual collection. To create a new collector
you use one of the built-in ones, and combine them using zip
.
import arrow.collectors.Collectors
import arrow.collectors.collect
import arrow.collectors.zip
fun divide(x: Int, y: Int): Double = x.toDouble() / y.toDouble()
val averageCollector = zip(Collectors.sum, Collectors.length, ::divide)
You then may apply the collector to the sequence or collection you want.
val average = list.collect(averageCollector)
The API implemented in arrow-collectors
is heavily influenced by
Java's Collector
and Haskell's foldl
library.