03.01.2014

Validation in Scala Part 1: The return type candidates

Introduction

In every project you need validation of user input, scala projects are no exception. In Java some people use Bean Validation to annotate their objects and have them validated by a validator instance.
So some months ago we also used Java Bean Validation in our Finagle based Scala project.
Unfortunately this wasn't a good choice for our distributed service architecture. We're using several services to do validations and we had to wrap these calls in Futures and had to "await" them inside our custom BeanValidations which causes threads to be blocked. Not a very reactive way for today!

Also the BeanValidation API is somehow not so nice to use, e.g. it forces you to annotate classes and we needed more flexibility than we expected, so to make a long story short: We moved away from it.

Now I've tried to move to a more functional and flexible solution, inspired by excellent posts like http://blog.lunatech.com/2012/03/02/validation-scala

What to use?

Keep it simple, use simple validation methods

We're back using simple validation methods like

  def validateUserRegistration(request: UserRegistration)

So the interesting part here is the return type to be used for returning either a success or a failure(s).

Here's my current choice list
  • Option
  • \/ (Scalaz Either)
  • Validation (also from Scalaz)
  • ValidationNel (a specialized Validation)
We will now look into them in a little bit more detail.

Option

Option is the simplest solution, it may contain either a None (representing e.g. something like "NotFound) or a Some(x) for the Success case. You may be tempted to use a Some(error) and a None for expressing "No failure" but I always regard this a little bit of irritating. Later in the conversion section we'll see that I'm not alone with this.
The nice thing is that Option is a Monad, you may use it in a for comprehensions like this:
You'll get either a Some if every step contains a Some or a None if any of the Options used in the for comprehensions are a None.

 

\/ (Scalaz's Either implementation)

As I already explained in another post we make use of Scalaz Either (written as \/ ), instead of the plain Scala Either, because you can combine them with other Monads (like Twitter futures) using Monad Transformers.

Besides that an \/ instance is either a Left (per convention) the bad case or a Right (per convention) the good case.
You create them like this:
Little bit funny at first that with ".left" and ".right" you'll specify the other type (right type for a ".left", left type for a ".right"), but it makes sense because you have to help the compiler with the non-present type, doesn't it?

Validation

Validation is pretty much like EitherZ but Success and Failure is not a convention any more but is expressed in the subtypes: Failure and Success.
Here's an example creating them:

ValidationNel

"Nel" stands for NonEmptyList, as the scaladoc says: A singly-linked list that is guaranteed to be non-empty. Why is this important?
Imagine the following:

  def registerUser(name: String,...): Validation[List[RegisterUserFailures], User]

The returned “List” of failures may be possibly empty with this signature. This is irritating, because what does this mean? So Scalaz helps us with a simple but nice trick:


Conversions between the above types

The nice thing that you may choose whatever best fitting type of the above for you validation method. If you later on compose several validation methods which use different return types of the above you may easily convert between them and use the best fitting type in your aggregation method:


The "fold" from Option to \/ is one of several ways to "enrich" the missing Left information in an Option. If you go back from \/ to Option with "toOption" the Left information from a -\/ is simply dropped and "mapped" to a None. So like I said above: "None" of an Option is usually considered an error case (without detailed information). The toOption method of \/ has the same viewpoint here.

In Part 2 of this blog we'll have a look at fail-fast strategy for validation or aggregating several validation failures.