NonEmptyList
is a data type used in Λrrow to model ordered lists that have at least one value.
NonEmptyList
is available in the arrow-core-data
module under the import arrow.core.NonEmptyList
// gradle
compile "io.arrow-kt:arrow-core-data:$arrow_version"
// namespace
import arrow.core.*
A NonEmptyList
guarantees the list always has at least 1 element.
NonEmptyList.of(1, 2, 3, 4, 5) // NonEmptyList<Int>
NonEmptyList.of(1, 2) // NonEmptyList<Int>
//NonEmptyList.of() // does not compile
Unlike List#[0]
, NonEmptyList#head
is a safe operation that guarantees no exception throwing.
NonEmptyList.of(1, 2, 3, 4, 5).head
When we fold over a NonEmptyList
, we turn a NonEmptyList< A >
into B
by providing a seed value and a function that carries the state on each iteration over the elements of the list.
The first argument is a function that addresses the seed value, this can be any object of any type which will then become the resulting typed value.
The second argument is a function that takes the current state and element in the iteration and returns the new state after transformations have been applied.
fun sumNel(nel: NonEmptyList<Int>): Int =
nel.foldLeft(0) { acc, n -> acc + n }
sumNel(NonEmptyList.of(1, 1, 1, 1))
// 4
map
allows us to transform A
into B
in NonEmptyList< A >
NonEmptyList.of(1, 1, 1, 1).map { it + 1 }
// NonEmptyList(2, 2, 2, 2)
flatMap
allows us to compute over the contents of multiple NonEmptyList< * >
values
val nelOne: NonEmptyList<Int> = NonEmptyList.of(1)
val nelTwo: NonEmptyList<Int> = NonEmptyList.of(2)
nelOne.flatMap { one ->
nelTwo.map { two ->
one + two
}
}
// NonEmptyList(3)
Λrrow allows imperative style comprehensions to make computing over NonEmptyList
values easy.
import arrow.typeclasses.*
import arrow.core.extensions.*
val nelOne: NonEmptyList<Int> = NonEmptyList.of(1)
val nelTwo: NonEmptyList<Int> = NonEmptyList.of(2)
val nelThree: NonEmptyList<Int> = NonEmptyList.of(3)
NonEmptyList.fx {
val (one) = nelOne
val (two) = nelTwo
val (three) = nelThree
one + two + three
}
// NonEmptyList(6)
Monad binding in NonEmptyList
and other collection related data type can be used as generators
NonEmptyList.fx {
val (x) = NonEmptyList.of(1, 2, 3)
val (y) = NonEmptyList.of(1, 2, 3)
x + y
}
// NonEmptyList(2, 3, 4, 3, 4, 5, 4, 5, 6)
Λrrow contains methods that allow you to preserve type information when computing over different NonEmptyList
typed values.
import arrow.core.*
import java.util.*
data class Person(val id: UUID, val name: String, val year: Int)
// Note each NonEmptyList is of a different type
val nelId: NonEmptyList<UUID> = NonEmptyList.of(UUID.randomUUID(), UUID.randomUUID())
val nelName: NonEmptyList<String> = NonEmptyList.of("William Alvin Howard", "Haskell Curry")
val nelYear: NonEmptyList<Int> = NonEmptyList.of(1926, 1900)
nelId.zip(nelName, nelYear) { id, name, year ->
Person(id, name, year)
}
// NonEmptyList(Person(id=fce79552-d0d0-43f3-9a9c-4f77f610e6f6, name=William Alvin Howard, year=1926), Person(id=6be5a026-71ae-4679-8065-829728e39370, name=Haskell Curry, year=1900))
NonEmptyList
is used to model lists that guarantee at least one elementNonEmptyList
with NonEmptyList.of
foldLeft
, map
, flatMap
, and others are used to compute over the internal contents of a NonEmptyList
value.fx { ... } comprehensions
can be used to imperatively compute over multiple NonEmptyList
values in sequence.NonEmptyList.applicative().map { ... }
can be used to compute over multiple NonEmptyList
values preserving type information and abstracting over arity with map
Do you like Arrow?
✖