The Chris Validation Pattern

After nosing through some Scala FP books I have been trying hard to write small and simple functions and then compose them together. This post will show an example of where this has taken some needlessly complex code into something terser and easier to understand.

Previously

When getting our feet wet with Scala we would more or less still write fairly traditional OO code with some nice functional goodies mixed in.

This is one of Scala's great strengths and it creates a relatively low barrier of entry for OO programmers; but there is a wealth of cool stuff you can achieve with it without having to blow the dust off your copy of The Gang of Four.

In this scenario we are re-implementing an old piece of functionality, which is user registration validation. The old Scala code to be fair it's completely fine. It's unit testable, fairly easy to understand and gets the job done.

My colleague and I (also named Chris); when re-implementing this feature for a new shared service thought we could use our growing Scala & FP skills to make something a bit nicer; and to show off. I think it's important not to rest on your laurels with Scala and really try new things out when possible. It's not just Java without semicolons.

The Problem

You wish to apply a number of validation rules against something and have a list of error messages to return eventually to a client.

def validatePassword(password: String): Set[ErrorMessages]

The old code

For the first iteration of code there was a class for validation which had one public method which took the item you wished to validate. Within the class were a number of private functions that took the item and checked to see if the item was valid and returned a sequence of errors.

The respective functions are all called at the public function and the sequences are mashed together into one sequence of errors.

class PasswordValidator{

    private def valFunc1(password: Password): Set[Error]

    private def valFunc2(password: Password): Set[Error]

    etc..

    def validate(password: Password): Set[Errors] = {
        valFunc1 ++ valFunc2 ++ valFunc3 ++ etc..
    }
}

The problems with this are :

  • The validation functions have two responsibilities, applying the business rule and then returning a collection of errors when appropriate. By coupling these responsibilities the functions become less re-usable.
  • Imagine if the private functions were public so you could unit test them. To unit test the sub functions which contain the actual business logic, you need to check for the error message returned, rather than whether the field passes the validation or not.
  • It doesn't look enough like a regular expression, which is how you know you are winning at Scala :)

Our Approach

When reading most FP books, they usually emphasise the idea of creating small "pure" functions which you then compose together to achieve greater pieces of functionality.

The ultimate solution: The Chris Validation Pattern

As I said, the colleague I did this was also called Chris so it's not an entirely egotistical name. Anyway ...

By thinking in terms of functions as being "pure" and only having one purpose, the resulting code was created fairly organically.

The first goal was to make it so the validation functions only dealt with applying a rule and letting the caller know if it passed or not

type StringValFunc = (String) => Boolean

def passwordIsTooShort:StringValFunc = 
    _.size < MIN_SIZE
def passwordIsTooLong:StringValFunc = 
    _.size > MAX_SIZE
def passwordNeedsInteger:StringValFunc = 
    !_.exists(_.isDigit)

//etc..

This means that unit testing these functions becomes very easy and not dependant on the error messages. Now that they are decoupled from any domain specific error messages; you could see how they could be refactored into something more generic, such as "stringIsTooShort".

The next task is coupling each function with an error message, which is obviously done by creating a map.

val passwordValidation = Map(
        passwordIsTooShort -> TooShortError,
        passwordIsTooLong -> TooLongError,
        etc
)

You then simply run all the functions against the value you are testing, filter out ones that didn't pass and then take the right side of the Map; which results in your list of error messages.

passwordValidation.filter(_._1(input)).map(_._2)

Ok, so to a non-scala developer it may not be completely obvious what that line does; but if you read into it, you can see it's only really performing two operations over a map.

Granted, this isn't the most impressive piece of code but I think it illustrates well how by being really strict in the responsibility of your functions and decoupling things you can compose useful functionality elegantly.

Edit

Thanks to Dan for pointing out that you can use the values method of a map to retrieve the values rather than the wonky .map(_._2). I also found that filterKeys can make this code more readable too.

So, ultimate version:

passwordValidation.filterKeys(_(input)).values