Watch video

Video tutorial

NonEmptyList

beginner

NonEmptyList is a data type used in Λrrow to model ordered lists that guarantee to have at least one value. NonEmptyList is available in the arrow-data module under the import arrow.data.NonEmptyList

// gradle
compile "io.arrow-kt:arrow-data:$arrow_version"
// namespace
import arrow.data.*

of

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 it’s a safe operation that guarantees no exception throwing.

NonEmptyList.of(1, 2, 3, 4, 5).head

foldLeft

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

map allows us to transform A into B in NonEmptyList< A >

NonEmptyList.of(1, 1, 1, 1).map { it + 1 }
// NonEmptyList(all=[2, 2, 2, 2])

flatMap

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(all=[3])

Monad binding

Λrrow allows imperative style comprehensions to make computing over NonEmptyList values easy.

import arrow.typeclasses.*
import arrow.instances.*

val nelOne: NonEmptyList<Int> = NonEmptyList.of(1)
val nelTwo: NonEmptyList<Int> = NonEmptyList.of(2)
val nelThree: NonEmptyList<Int> = NonEmptyList.of(3)

ForNonEmptyList extensions {
  binding {
    val one = nelOne.bind()
    val two = nelTwo.bind()
    val three = nelThree.bind()
    one + two + three
  }.fix()
}
// NonEmptyList(all=[6])

Monad binding in NonEmptyList and other collection related data type can be used as generators

ForNonEmptyList extensions {
  binding {
    val x = NonEmptyList.of(1, 2, 3).bind()
    val y = NonEmptyList.of(1, 2, 3).bind()
    x + y
  }.fix()
}
// NonEmptyList(all=[2, 3, 4, 3, 4, 5, 4, 5, 6])

Applicative Builder

Λrrow contains methods that allow you to preserve type information when computing over different NonEmptyList typed values.

import arrow.data.*
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)

ForNonEmptyList extensions {
 map(nelId, nelName, nelYear, { (id, name, year) ->
  Person(id, name, year)
 })
}
// NonEmptyList(all=[Person(id=3fcf92fa-d1f7-4cff-a932-80c1044b6c2a, name=William Alvin Howard, year=1926), Person(id=3fcf92fa-d1f7-4cff-a932-80c1044b6c2a, name=Haskell Curry, year=1926), Person(id=bb2801c9-2a9b-4d0d-b9d0-67e2339f2a64, name=William Alvin Howard, year=1926), Person(id=bb2801c9-2a9b-4d0d-b9d0-67e2339f2a64, name=Haskell Curry, year=1926), Person(id=3fcf92fa-d1f7-4cff-a932-80c1044b6c2a, name=William Alvin Howard, year=1900), Person(id=3fcf92fa-d1f7-4cff-a932-80c1044b6c2a, name=Haskell Curry, year=1900), Person(id=bb2801c9-2a9b-4d0d-b9d0-67e2339f2a64, name=William Alvin Howard, year=1900), Person(id=bb2801c9-2a9b-4d0d-b9d0-67e2339f2a64, name=Haskell Curry, year=1900)])

Summary

  • NonEmptyList is used to model lists that guarantee at least one element
  • We can easily construct values of NonEmptyList with NonEmptyList.of
  • foldLeft, map, flatMap and others are used to compute over the internal contents of a NonEmptyList value.
  • binding { ... } 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

Supported type classes

Module Type classes
arrow.aql Count, From, GroupBy, OrderBy, Select, Sum, Union, Where
arrow.mtl.typeclasses FunctorFilter
arrow.optics.typeclasses Each, FilterIndex, Index
arrow.typeclasses Applicative, Bimonad, Comonad, Eq, Foldable, Functor, Hash, Monad, Semigroup, SemigroupK, Show, Traverse