Skip to main content

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)
Influences

The API implemented in arrow-collectors is heavily influenced by Java's Collector and Haskell's foldl library.