A relatively short article, this time, to present an experimental feature developed by Sadek Drobi (@sadache) & I (@mandubian) and that we’ve decided to integrate into Play-2.1 because we think it’s really interesting and useful.
Remember how you write a
Reads[T] for a case class.
1 2 3 4 5 6 7 8 9 10
So you write 4 lines for this case class.
You know what? We have had a few complaints from some people who think it’s not cool to write a
Reads[TheirClass] because usually Java JSON frameworks like Jackson or Gson do it behind the curtain without writing anything.
We argued that Play2.1 JSON serializers/deserializers are:
- completely typesafe,
- fully compiled,
- nothing was performed using introspection/reflection at runtime.
But for some, this didn’t justify the extra lines of code for case classes.
We believe this is a really good approach so we persisted and proposed:
- JSON simplified syntax
- JSON combinators
- JSON transformers
Added power, but nothing changed for the additional 4 lines.
As we are perfectionist, now we propose a new way of writing the same code:
1 2 3 4 5 6
1 line only.
Questions you may ask immediately:
Does it use runtime bytecode enhancement? -> NO
Does it use runtime introspection? -> NO
Does it break type-safety? -> NO
As I’m currently in a mood of creating new expressions, after creating JSON coast-to-coast design, let’s call it JSON INCEPTION (cool word, quite puzzling isn’t it? ;)
As explained just before:
1 2 3 4 5 6 7 8 9
Here is the equation describing the windy Inception concept:
Case Class Inspection
As you may deduce by yourself, in order to ensure preceding code equivalence, we need :
- to inspect
- to extract the 3 fields
lovesChocolateand their types,
- to resolve typeclasses implicits,
- to find
INJECTION??? Injjjjjectiiiiion….??? injectionnnnnnnn????
No I stop you immediately…
Code injection is not dependency injection…
No Spring behind inception… No IOC, No DI… No No No ;)
I used this term on purpose because I know that injection is now linked immediately to IOC and Spring. But I’d like to re-establish this word with its real meaning.
Here code injection just means that we inject code at compile-time into the compiled scala AST (Abstract Syntax Tree).
Json.reads[Person] is compiled and replaced in the compile AST by:
1 2 3 4 5
Nothing less, nothing more…
Yes everything is performed at compile-time.
No runtime bytecode enhancement.
No runtime introspection.
As everything is resolved at compile-time, you will have a compile error if you did not import the required implicits for all the types of the fields.
We needed a Scala feature enabling:
- compile-time code enhancement
- compile-time class/implicits inspection
- compile-time code injection
This is enabled by a new experimental feature introduced in Scala 2.10: Scala Macros
Scala macros is a new feature (still experimental) with a huge potential. You can :
- introspect code at compile-time based on Scala reflection API,
- access all imports, implicits in the current compile context
- create new code expressions, generate compiling errors and inject them into compile chain.
Please note that:
- We use Scala Macros because it corresponds exactly to our requirements.
- We use Scala macros as an enabler, not as an end in itself.
- The macro is a helper that generates the code you could write by yourself.
- It doesn’t add, hide unexpected code behind the curtain.
- We follow the no-surprise principle
As you may discover, writing a macro is not a trivial process since your macro code executes in the compiler runtime (or universe).
So you write macro code that is compiled and executed in a runtime that manipulates your code to be compiled and executed in a future runtime…
That’s also certainly why I called it Inception ;)
So it requires some mental exercises to follow exactly what you do. The API is also quite complex and not fully documented yet. Therefore, you must persevere when you begin using macros.
I’ll certainly write other articles about Scala macros because there are lots of things to say.
This article is also meant to begin the reflection about the right way to use Scala Macros.
Great power means greater responsability so it’s better to discuss all together and establish a few good manners…
Please remark that JSON inception just works for case class having
Naturally, you can also incept
1 2 3 4
1 2 3 4
With the so-called JSON inception, we have added a helper providing a trivial way to define your default typesafe
Reads[T]/Writes[T]/Format[T]for case classes.
If anyone tells me there is still 1 line to write, I think I might become unpolite ;)