For a server-client application I need a way to automatically check the conformity of data structures used for communication. To achieve this I need to compare the serializable representations of those data structures. What I basically expect is something that would construct a type representation tree with primitive types as leafs.
E.g., the type Artist
from the following data model:
data Artist = Artist Text Genre
data Genre = Jazz | Metal
would be represented as something like:
DataType
"Artist"
[
Constructor
"Artist"
[
AbstractType "Data.Text.Text",
DataType
"Genre"
[
Constructor "Jazz" [],
Constructor "Metal" []
]
]
]
Is there any library that implements such functionality and are there any better approaches to this problem? E.g., how do they approach this in Cloud Haskell?
I have just released a type-structure library, which approaches exactly the issue declared in the subject. It constructs a data-representation of a type, while traversing all the types it refers to down to the primitives. Thus it transitively captures the changes to all types involved, which may even come from different libraries.
The produced graph has a Hashable
instance, so it can be used to perform the matching aswell. E.g., one can produce a "version" hash with it.
Now, since the implementation uses typeclasses with CAF implementations, the construction of the representation data should be done in O(1). I have to mention though, that I haven't benchmarked it.
BTW, since types can be recursive, the library couldn't be implemented the way it was expected in the question, because it would construct an infinite tree otherwise. Instead the library represents the data structure as a graph. In fact, this graph is itself represented as a dictionary of edges, since there's no better way to represent an immutable graph.
Here's a GHCi session, showing, how the library is supposed to be used:
λ>import TypeStructure
Construction of the type structure representation graph:
λ>graph (undefined :: Int)
(Type_Con ("GHC.Types","Int"),[(("GHC.Types","Int"),Declaration_ADT [] [("I#",[Type_Con ("GHC.Prim","Int#")])]),(("GHC.Prim","Int#"),Declaration_Primitive)])
Graphs of different types are guaranteed to be different:
λ>graph (undefined :: Int) /= graph (undefined :: Integer)
True
Graphs of values of the same type are guaranteed to be the same:
λ>graph True == graph False
True
Acquiring a hash of the typestructure:
λ>import Data.Hashable
λ>hash $ graph (undefined :: Int)
3224108341943761557
Hashes of different types should not be equal:
λ>(hash $ graph (undefined :: Int)) /= (hash $ graph (undefined :: Integer))
True
Our generic serialization library beamable does this (otherwise it's a similar function to cereal
or binary
). Look for the function typeSign
. The default encode
/decode
pair doesn't do type signing, but encodeLive
will, or you can sign data types yourself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With