Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A serializable representation of a data type for client-server conformity checking

Tags:

haskell

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?

like image 961
Nikita Volkov Avatar asked Oct 02 '22 04:10

Nikita Volkov


2 Answers

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.

How to use

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
like image 106
Nikita Volkov Avatar answered Oct 13 '22 12:10

Nikita Volkov


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.

like image 37
John L Avatar answered Oct 13 '22 12:10

John L