8c6794b6.github.io

Serialization in a hurry

I need to serialize below data in 5 minutes:

| data Foo = Foo
|  { one   :: Int
|  , two   :: (Maybe Double, String)
|  , three :: Vector Double
|  }

Requires ghc-7.2.2 or above, and cereal package.

> {-# LANGUAGE DeriveGeneric #-}
> module SerializationInAHurry where

We are using DeriveGeneric language pragma.

> import GHC.Generics
> import Data.Serialize
> import Data.Vector (Vector(..))
> import qualified Data.ByteString as B
> import qualified Data.Vector as V

The target data type to serialize.

> data Foo = Foo
>  { one   :: Int
>  , two   :: (Maybe Double, String)
>  , three :: Vector Double
>  } deriving (Generic, Show, Eq)

And sample data with target data type.

> foo1 :: Foo
> foo1 = Foo
>   { one   = 100
>   , two   = (Just 8.5, "eight point five")
>   , three = V.fromList [11,13..21]
>   }

As haddock document in cereal package notes, when the target data type has Generic instance, Serialize class will use it. What we need is to make instance of Serialize with empty body.

Data.Generics.Generic class has couple instances already defined.

| instance Generic [a] -- Defined in `GHC.Generics'
| instance Generic Ordering -- Defined in `GHC.Generics'
| instance Generic Int -- Defined in `GHC.Generics'
| instance Generic Float -- Defined in `GHC.Generics'
| instance Generic Double -- Defined in `GHC.Generics'
| instance Generic Char -- Defined in `GHC.Generics'
| instance Generic Bool -- Defined in `GHC.Generics'
| instance Generic (Maybe a) -- Defined in `Data.Maybe'
| instance Generic (Either a b) -- Defined in `Data.Either'
| instance Generic (a, b, c, d, e, f, g) -- Defined in `GHC.Tuple'
| instance Generic (a, b, c, d, e, f) -- Defined in `GHC.Tuple'
| instance Generic (a, b, c, d, e) -- Defined in `GHC.Tuple'
| instance Generic (a, b, c, d) -- Defined in `GHC.Tuple'
| instance Generic (a, b, c) -- Defined in `GHC.Tuple'
| instance Generic (a, b) -- Defined in `GHC.Tuple'
| instance Generic () -- Defined in `GHC.Tuple'

The only missing definition we need is:

> instance Serialize a => Serialize (Vector a) where
>   put = put `fmap` V.toList
>   get = V.fromList `fmap` get

There should be other way which shows better performance for serializing Vector, but recall the title of this post. Now we are ready to serialize.

> instance Serialize Foo

Testing it with writing to file, reading from the file and comparing with original:

> main :: IO ()
> main = do
>   let file = "foo1.data"
>   B.writeFile file (encode foo1)
>   res <- decode `fmap` B.readFile file :: IO (Either String Foo)
>   case res of
>      Right foo1' -> print $ foo1 == foo1'
>      Left  err   -> error err

Result:

| ghci> main
| True
TAGGED: haskell, generic, ghc