In this post we’ll look at NonEmptyList which is a singly-linked list guaranteed to be non-empty. Let’s begin by seeing how using a NEL (short for NonEmptyList
) in appropriate places is better than using regular List
.
Contrasting List with NEL
1 | @ def email(body: String, recipients: List[String]): Unit = { |
Let’s say we’re writing an email
function that sends email to a list of people. A naive assumption would be to always expect recipients
to have at least one element. However, we may end up with an empty list and that’ll lead to painstaking debugging trying to find out why emails weren’t sent out.
So, we’ll change our email
function to this:
1 | @ def email(body: String, recipients: List[String]): Either[String, String] = { |
Now although we’re handling the case of empty list, we’ve introduced unnecessary noise with an if
-else
. Here’s how we can get a guarantee on being non-empty and avoid the conditional:
1 | @ import scalaz._ |
Voila! Using a NEL gives us two advantages:
- A guarantee on never receiving an empty list
- Expressing our intent in the function signature
With that said, let’s look at how we can create and use NELs.
Creating a NEL
1 | @ NonEmptyList(1, 2, 3, 4) |
In the example above, we’re using the apply
method to create a NEL. It looks like the following:
1 | def apply[A](h: A, t: A*) |
The first argument is the head whereas the second argument is varargs which becomes the tail. Internally, the apply
method calls the nels
method. We can use that directly, too.
1 | @ NonEmptyList nels(1, 2, 3, 4) |
You can also create a NEL from a single value by using wrapNel
.
1 | @ 1.wrapNel |
You can even create a NEL in the cons list way like you’d do with a List
1 | // good ol' List |
You can convert a List
to a NEL by calling toNel
. This will yield an Option
of NEL since the List
may be empty
1 | @ List(1, 2, 3) toNel |
Appending NELs
1 | @ NonEmptyList(1, 2, 3) append NonEmptyList(4, 5, 6) |
You can also concatenate two NELs by using append
.
Head and Tail
1 | @ NonEmptyList(1, 2, 3).head |
head
and tail
return and head and tail of the NEL, respectively. Note that head
is guaranteed to work without exceptions because we’ll always have one element.
Iterating
1 | @ NonEmptyList(1, 2, 3) foreach println |
Because NEL is also a list, you can perform familiar list operations like foreach
, map
, etc.
Conclusion
This post was intended to give you an introduction to Scalaz NonEmptyList
and how it differs from the usual List
. The guarantee on NEL being non-empty means that you can call head
without having to worry about exceptions. This makes your code less cluttered and its intent more expressive.