binary-typed- Type-safe binary serialization

Safe HaskellSafe-Inferred




This meta-module exists only for documentational purposes; the library functionality is found in Data.Binary.Typed.



Standard Binary serializes to ByteString, which is an untyped format; deserialization of unexpected input usually results in unusable data.

This module defines a Typed type, which allows serializing both a value and the type of that value; deserialization can then check whether the received data was sent assuming the right type, and error messages may provide insight into the type mismatch.

For example, this uses Binary directly:

test1 = let val = 10 :: Int
            enc = encode val
            dec = decode enc :: Bool
        in  print dec

This behaves unexpectedly: An Int value is converted to a Bool, which corresponds to a wacky type coercion. The receiving end has no way of knowing what the incoming data should have been interpreted as.

Using Typed, this can be avoided:

test2 = let val = 10 :: Int
            enc = encode (typed Full val)
            dec = decode enc :: Typed Bool
        in  print dec

This time decode raises an error: the incoming data is tagged as an Int, but is attempted to be decoded as Bool.

Basic usage

This package is typically used for debugging purposes. Hashed type information keeps the size overhead relatively low, but requires a certain amount of computational ressources. It is reliable at detecting errors, but not very good at telling specifics about it. If a problem is identified, the typing level can be increased to Shown or Full, providing information about the involved types. If performance is critical, Untyped "typed" encoding can be used, with minimal overhead compared to using Binary directly.

For convenience, this module exports a couple of convenience functions that have the type-mangling baked in already. The above example could have been written as

test3 = let val = 10 :: Int
            enc = encodeTyped val
            dec = decodeTyped enc :: Either String Bool
        in  print dec

However, using encodeTyped is computationally inefficient when many messages of the same type are serialized, since it recomputes a serialized version of that type for every single serialized value from scratch. encodeTypedLike exists to remedy that: it takes a separately constructed Typed dummy value, and computes a new serialization function for that type out of it. This serialization function then re-uses the type representation of the dummy value, and simply replaces the contained value on each serialization so that no unnecessary overhead is introduced.

-- Computes Ints type representation 100 times:
manyIntsNaive = map encodeTyped [1..100 :: Int]

-- Much more efficient: prepare dummy value to precache the
-- type representation, computing it only once:
encodeInt = encodeTypedLike (typed Full (0 :: Int))
manyIntsCached = map encodeInt [1..100]

API overview

The core definitions in Data.Binary.Typed are:

In addition to those, a couple of useful helper functions with more efficient implementation than what the core definitions could offer:

  • mapTyped (change values contained in Typeds)
  • reValue (change value, but don't recompute type representation)
  • reType (change type representation, but keep value)
  • precache (compute serialized type representation, useful as an optimization)

Lastly, there are a number of encoding/decoding functions, mostly for convenience: