The code & sample apps can be found on Github
After 5 months studying theories deeper & deeper on my free-time and preparing 3 talks for scala.io & ping-conf with my friend Julien Tournay aka @skaalf, I’m back blogging and I’ve got a few more ideas of articles to come…
If you’re interested in those talks, you can find pingconf videos here:
Let’s go back to our today’s subject : Incoming Play2.3/Scala generic validation API & more.
Julien Tournay aka @skaalf has been working a lot for a few months developing this new API and has just published an article previewing Play 2.3 generic validation API.
This new API is just the logical extension of play2/Scala Json API (that I’ve been working & promoting those 2 last years) pushing its principles far further by allowing validation on any data types.
This new API is a real step further as it will progressively propose a common API for all validations in Play2/Scala (Form/Json/XML/…). It proposes an even more robust design relying on very strong theoretical ground making it very reliable & typesafe.
Julien has written his article presenting the new API basics and he also found time to write great documentation for this new validation API. I must confess Json API doc was quite messy but I’ve never found freetime (and courage) to do better. So I’m not going to spend time on basic features of this new API and I’m going to target advanced features to open your minds about the power of this new API.
Let’s have fun with this new APi & Shapeless, this fantastic tool for higher-rank polymorphism & type-safety!
Warm-up with Higher-kind Zipping of Rules
A really cool & new feature of Play2.3 generic validation API is its ability to compose validation Rules in chains like:
1 2 3 4 |
|
In Play2.1 Json API, you couldn’t do that (you could only map on Reads).
Moreover, with new validation API, as in Json API, you can use macros to create basic validators from case-classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Great but sometimes not enough as you would like to add custom validations on your class. For example, you want to verify :
foo
isn’t emptybar
is >5foo2
is <10
For that you can’t use the macro and must write your caseclass Rule yourself.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Please note the new From[JsValue]
: if it were Xml, it would be From[Xml]
, genericity requires some more info.
Ok that’s not too hard but sometimes you would like to use first the macro and after those primary type validations, you want to refine with custom validations. Something like:
1 2 |
|
As you may know, you can’t do use this +:
from Scala Sequence[T]
as this list of Rules is typed heterogenously and Rule[I, O]
is invariant.
So we are going to use Shapeless heterogenous Hlist for that:
1 2 3 4 5 6 |
|
How to compose Rule[JsValue, FooBar]
with Rule[String, String]) :: Rule[Int, Int] :: Rule[Long,Long]
?
We need to convert Rule[JsValue, FooBar]
to something like Rule[JsValue, T <: HList]
.
Based on Shapeless Generic[T]
, we can provide a nice little new conversion API .hlisted
:
1
|
|
Generic[T]
is able to convert any caseclass from Scala from/to Shapeless HList (& CoProduct).
So we can validate a case class with the macro and get a Rule[JsValue, T <: HList]
from it.
How to compose Rule[JsValue, String :: Int :: Long :: HNil]
with Rule[String, String]) :: Rule[Int, Int] :: Rule[Long,Long]
?
Again, using Shapeless Polymorphic and HList RightFolder, we can implement a function :
1 2 |
|
This looks like some higher-kind zip function, let’s call it
HZIP
.
Now, we can compose them…
1 2 3 4 5 6 |
|
Finally, let’s wire all together in a Play action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
As you can see, the problem in this approach is that we lose the path of Json. Anyway, this can give you a few ideas! Now let’s do something really useful…
Higher-kind Fold of Rules to break the 22 limits
As in Play2.1 Json API, the new validation API provides an applicative builder which allows the following:
1 2 3 |
|
But, in Play2.1 Json API and also in new validation API, all functional combinators are limited by the famous Scala 22 limits.
In Scala, you CAN’T write :
- a case-class with >22 fields
- a
Tuple23
So you can’t do Rule[JsValue, A] ~ Rule[JsValue, B] ~ ...
more than 22 times.
Nevertheless, sometimes you receive huge JSON with much more than 22 fields in it. Then you have to build more complex models like case-classes embedding case-classes… Shameful, isn’t it…
Let’s be shameless with Shapeless HList which enables to have unlimited heterogenously typed lists!
So, with HList, we can write :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
|
That’s cool but we want the ::
operator to have the same applicative builder behavior as the
~/and` operator:
1 2 |
|
This looks like a higher-kind fold so let’s call that
HFOLD
.
We can build this hfold
using Shapeless polymorphic functions & RighFolder.
In a next article, I may write about coding such shapeless feature. Meanwhile, you’ll have to discover the code on Github as it’s a bit hairy but very interesting ;)
Gathering everything, we obtain the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
|
Let’s write a play action using this rule:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
|
Awesome… now, nobody can say 22 limits is still a problem ;)
Have a look at the code on Github.
Have fun x 50!