Easy validation in Scala using Scalaz, Readers and ValidationNel

I'm working on a new book for Packt which shows how you can use various Scala frameworks to create REST services. For this book I'm looking at the following list of frameworks: Finch, Unfiltered, Scalatra, Spray and Akka-HTTP (reactive streams).
While writing the chapters on Finch and Unfiltered I really liked the way these two frameworks handled validation. They both use composable validators which allow you to create easy to use validation chains, that collect any validation errors.

In this article I'll show you how you can use such an approach yourself using the Scalaz library, and more specifically on how the Reader monad and the Validation applicative can be combined to create flexible, compassable validators, that accumulate any validation errors. As an example we'll create a number of Readers that you can use to process an incoming HTTPRequest, get some parameters and convert it to a case class.

Getting started

The complete example can be found in the following Gist. All the code fragments in this article, are shown directly from that Gist. First, though, for this project we of course use SBT. Since we'll only be using Scalaz, we have a very simple SBT build file:

To work with Scalaz, you can either import the required packages and functions individually, or, as we do in this case, be lazy and just import everything. So at the top of our example we'll add the following:

Before we'll look at the readers themselves, lets first define our case class and some type aliases to make things easier to understand:

The Reader monad

Before we start creating our own readers, lets look a bit closer at what a reader monad allows you to do. We'll look at the example from here (great introduction into Scalaz): http://eed3si9n.com/learning-scalaz-day10

scala> def myName(step: String): Reader[String, String] = Reader {step + ", I am " + _}
myName: (step: String)scalaz.Reader[String,String]
scala> def localExample: Reader[String, (String, String, String)] = for {
         a <- myName("First")
         b <- myName("Second") >=> Reader { _ + "dy"}
         c <- myName("Third")  
       } yield (a, b, c)
localExample: scalaz.Reader[String,(String, String, String)]
scala> localExample("Fred")
res0: (String, String, String) = (First, I am Fred,Second, I am Freddy,Third, I am Fred)

The point of a reader monad is to supply some configuration object (for instance a HTTPRequest, a DB, or anything else you can inject) without having to manually (or implicitly) pass it around all the functions. As you can see from this example we create three Readers (by calling myName) and pass in the request just once to the composed result. Note that for comprehension only works when all the readers are of the same type. In our case we have strings and ints so we use a somewhat different syntax to compose the readers as we'll see later. The basic idea, however, is the same. We define a number of readers, and pass in the request to be processed.

Our Readers

We've defined our readers in a helper object, to make using them a bit easier. First let's look at the complete code:

What we do here is define a single as[T] method with an implicit to method, that returns a RequestReader of the specified type. Through the use of implicits scala will use one of the asString, AsInt etc. methods to determine how to convert the passed in key to a correct reader. Let's look a bit closer at the asInt and the keyFromMap function.

The asInt function creates a new Reader and uses the request that is passed in to call the keyFromMap function. This function tries to get the value from the Map, and if it is successful it return a Success, if not a Failure. Next we flatMap this result (only applies when the result is a Success) and try to convert the found value to an integer using the scalar provided parseInt function, which in turn also returns a Validation. The result from this function is passed on to the toMessage function which transforms a Validation[Throwable, S] to a Validation[String, s]. Finally, before returning, we use the toValidationNel function to convert the Validation[String, Int] to a ValidationNel[String, Int]. We do this so that we can more easily collect all the failures together.

Creating a new validation, just means creating a new reader that returns a ValidationNel[String, T].

Composing validations

Now lets look at how we can compose the validations together. To do this we can use the ApplicativeBuilder from Scalaz like this:

Using the |@| symbol we combine our readers using the scalaz ApplicativeBuilder. Using the tupled function we return a list of tuples containing the individual results of our readers, when they've run. To run this reader, we need to supply it with a request:

The result of these calls look like this:

 (Success(Amber),Failure(NonEmptyList(Key: last Not found)),Failure(NonEmptyList(Exception: class java.lang.NumberFormatException msg: For input string: "20 Months"))) 

Even though this approach already allows us to create and compose validations and return the individual successes and failures, it will still take some work to get either the failures or convert the values to our case class. Luckily though, we can also easily collect the success and failures, since we're using the ValidationNel object:

When this reader is run each individual validation will be applied, and passed into the provided apply function. In this function we collect the validations using the |@| constructor. This will return a Failure containing the collected errors, or an instantiated person if all the validations are successful:

Which results in this:

 Failure(NonEmptyList(Key: last Not found, Exception: class java.lang.NumberFormatException msg: For input string: "20 Months")) 

Cool right! This way we either get a success containing our domain object, or a single Failure object contain a list of errors.

The final part I'd like to show is an alternative way of collecting the validations. In the previous example we used the |@| syntax, you can also directly create an Applicative and use it to collect the validations:

And the output of this function is this:

 Failure(NonEmptyList(Key: last Not found, Exception: class java.lang.NumberFormatException msg: For input string: "20 Months"))

And that's it. To summarise the main points:

  • Using the reader pattern it is very easy to pass some context into functions for processing.
  • With the applicative builder or the |@| symbol, you can easily compose readers that continue even if the first one should fail.
  • By using a validationNel we can easily collect various validation results, and either return the collected errors, or return a single success.