Shapeless and Applicative Functors for Server side validations

(Work in progress, there are typos and inconsistencies, so excuse)

This is a quick deep dive into shapeless and Scalaz applicative builder pattern working together and solving a use-case for better understanding. You may well be tweaking further to get your stuff done based on what is presented here.

Audience

Problem(s)

Ok, so how many times have you hacked around validating fields each one returning an applicative and then write long boiler plates of using applicative builder pattern for each of your aggregates in the app? Something like this:

(column1.validate|@| column2.validate|@| column3.successNel[ValidationError]) {Entity.apply}

How many times have you re-written the validation logic for fields that are shared across aggregates, and then write applicative builders explicitly every time?

Or at the very least, how many times you were confused on where to put all of the boiler plates of applicative builders in your application?

Solution

When you provide validations (only) for every field values in your app that you use in your application:

* You get validation for all your entity objects, value objects and aggregates for FREE
* Return the list of errors or the validated entity for all your entities in your application with just one
|@| in your entire app.
* You get a compile time if you try to use a column that doesn’t have a validation instance in any entity or value objects (Well, this is not a big deal you know but sounds good though :D)

Let’s write some code

Let’s use tagged types instead of primitives just to differentiate your types are your own types :D

Let’s define an aggregate (may not make much sense, doesn’t matter)

Let’s define instances for each field

Ok, now we have instances of Validation for each field in SuperExchange. Now our intention is to get an instance for SuperExchange for free.

For that, first let’s create an instance for any HList, provided we have the validation instance for its the head and tail (which in turns has a validation instance for its head and tail and it goes on recursively until HNil).

Once we have the instance for HList (that means we have a validation instance for Generic.Repr ), and once we have a Generic instance for SuperExchange (which you get for Free from shapeless), we can derive the validation instance for SuperExchange.
Let’s have that in place:

You may note that, there is a bit going on with respect to gen.to and gen.from , but that is just us trying to satisfy the compiler work and it is not something far from the docs of shapeless. When you do env.validate(gen.to(b)) what you get in return is a Validation[Generic.Repr] and not for A (which is in fact going to be our aggregate or entity). To get the validation instance for A you map it further on the returned applicative and get A from Repr using gen.from

That’s it. Now that you have the instances freely available for any entities that use (and only use) ExchangeId and Name types. Let’s test this:

Let’s work it for other entities that re-uses some of the entities, and see if we can forget about applicative builders and be in a nice world.

Please note that we have given validation instance for that extra field newNumber , else we get a compile time error.

Conceptually it is very pleasing: “Provide validations for all the fields, and any entities that use them are automatically validated”.

Stare at these codes for a while, and I will probably come up with a better explanation of the concepts later on.

Thanks and have fun!

--

--

A software engineer and a functional programming enthusiast at Simple-machines, Sydney, and a hardcore hiking fan. https://twitter.com/afsalt2

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Afsal Thaj

A software engineer and a functional programming enthusiast at Simple-machines, Sydney, and a hardcore hiking fan. https://twitter.com/afsalt2