flat-0.6: Principled and efficient bit-oriented binary serialization.

Flat.Tutorial

Synopsis

# Documentation

To (de)serialise a data type, make it an instance of the Flat class.

There is Generics based support to automatically derive a correct instance.

Let’s see some code.

We need a couple of extensions:

>>> :set -XDeriveGeneric -XDeriveAnyClass


The Flat top module:

>>> import Flat


And, just for fun, a couple of functions to display an encoded value as a sequence of bits:

>>> import Flat.Instances.Test (flatBits,allBits)


Define a few custom data types, deriving Generic and Flat:

>>> data Result = Bad | Good deriving (Show,Generic,Flat)

>>> data Direction = North | South | Center | East | West deriving (Show,Generic,Flat)

>>> data List a = Nil | Cons a (List a) deriving (Show,Generic,Flat)


Now we can encode a List of Directions using flat:

>>> flat $Cons North (Cons South Nil) "\149"  The result is a strict ByteString. And decode it back using unflat: >>> unflat . flat$ Cons North (Cons South Nil) :: Decoded (List Direction)
Right (Cons North (Cons South Nil))


The result is a Decoded value: Either a DecodeException or the actual value.

### Optimal Bit-Encoding

A pecularity of Flat is that it uses an optimal bit-encoding rather than the usual byte-oriented one.

One bit is sufficient to encode a Result or an empty List:

>>> flatBits Good
"1"

>>> flatBits (Nil::List Direction)
"0"


Two or three bits suffice for a Direction:

>>> flatBits South
"01"

>>> flatBits West
"111"


For the serialisation to work with byte-oriented devices or storage, we need to add some padding.

To do so, rather than encoding a plain value, flat encodes a PostAligned value, that's to say a value followed by a Filler that stretches till the next byte boundary.

In practice, the padding is a, possibly empty, sequence of 0s followed by a 1.

For example, this list encodes as 7 bits:

>>> flatBits $Cons North (Cons South Nil) "1001010"  And, with the added padding of a final "1", will snugly fit in a single byte: >>> allBits$ Cons North (Cons South Nil)
"10010101"


But .. you don't need to worry about these details as byte-padding is automatically added by the function flat and removed by unflat.

### Pre-defined Instances

Flat instances are already defined for relevant types of some common packages: array, base, bytestring, containers, dlist, mono-traversable, text, unordered-containers, vector.

They are automatically imported by the Flat module.

For example:

>>> flatBits \$ Just True
"11"


### Wrapper Types

There are a few wrapper types that modify the way encoding and/or decoding occur.

• Flat.AsBin and Flat.AsSize decode to a value's flat binary representation or size in bits respectively.
• AsArray and AsList encode/decode a sequence as a List or Array respectively, see Flat.Instances.Mono for details.
• UTF8Text and UTF16Text encode/decode a Text as UTF8 or UTF16 respectively.