This is a sequel of previous post Sangria without case classes where I’m exploring shapeless in conjunction with the awesome Sangria lib. Mind this is total exploration and chances are (actually very likely) that it is possible to do simpler code to achieve the same effect.
A quick refresh of what is going on here. Sangria is a awesome GraphQL lib for scala. It uses case classes in the documentation and provide a set of macros to derive some stuff from them. What is great, but, I’m trying to expose dynamic content objects no known at compile time.
HMaps resembles C++ POCO Dynamic Var lib. I’m not comparing exactly, but this allows us to create dynamic type safe
structs at runtime. Of course the API is quite different and
ugly if compared to what shapeless did taking advantage of the scala type system.
For my scenario, I defined a parametrized class
class PersistentIS[K, V] and just three implicits to start. Let’s take a look:
Wow, looks like a bunch of empty classes. The first occurency of
HMap is in the implicit definition of the maping from
String to a
PersistentIS, that happens to be wrapped in a
HMap. In order to create a instance of our
PersistentIS[K, V], just do like in the last line of the sample code above.
Now it is time to define our GraphQL
There is a trick here. The resolvers are different and I’ll show why and how in a minute. First take a look at the
age resolve function. It is a normal Sangria way to resolve a field from a
Val stored in the
Context here named
ctx . That is, the query resolver returns a Value of type
HMap[PersistentIS] and this resolve function extracts the needed field from it.
The point is that we need to know the type of the result value (a
age) but also the name of the field. I wanted to get rid of both and use just
defaultResolve but so far I couldn’t wipe them. But at least the field name I managed to get rid of. Not that the code that I’m showing below has the sole purpose to get rid of the field name while extracting it from the
HMap[PersistentIS]. The main goal here is a more dynamic configuration of GraphQL as well as its resolve functions.
Here we go! A lot of code at first but in summary what is involved is a type class that defines a get method, instances of this type class for
String. And as a extra step the definition of the
Ok I know, looks like lots of code, etc, etc. But to be fair when you start working with type classes and concepts you also find in Haskell and Cats, you have a thinking shift. It is like you start to see code in multiple dimensions instead of the usual linear roll from the top to the bottom of the file and you are ok to write, read and luckily understand code.
The code above is commented, so no extra info to add. Now the most confusing part - must confess - of the code. The
Go back to the second snippet, you’ll quickly grasp that
defaultResolve[String] is actually returning a resolve function as required by Sangria. There is nothing special here actually. Just pay attention to the implicit getter that is resolved based on
[Res]. Using this getter we then extract the value from the context value, a
HMap[PersistentIS] and return it as
Res that happen to be
I wanted to remove the type parameter from
defaultResolve[String] and use just
defaultResolve but maybe after abstracting the whole generation of the GrpahQL
ObjectType this will work.
HMaps is quite powerful and using it caused no impact in the GrpahQL exposed API, everything works normal. It results in a more dynamic behavior when compared to
Records that requires a macro to generate the appropriate labeled
in love with shapeless. This thing is simply mind blowing. I thought I would be using labeled
HLists some how. But not sure if possible due to the lack of information at compile time to get singleton types, etc. Well, need to investigate more and hope this investigation can result in another post.