| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Data.Binary.Typed.Tutorial
Description
This meta-module exists only for documentational purposes; the library functionality is found in Data.Binary.Typed.
Motivation
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 ::Intenc =encodeval dec =decodeenc ::Boolin
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 ::Intenc =encode(typedFullval) dec =decodeenc ::TypedBoolin
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 ::Intenc =encodeTypedval dec =decodeTypedenc ::EitherStringBoolin
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.
-- ComputesInts type representation 100 times: manyIntsNaive = mapencodeTyped[1..100 ::Int] -- Much more efficient: prepare dummy value to precache the -- type representation, computing it only once:encodeInt=encodeTypedLike(typedFull(0 ::Int)) manyIntsCached = mapencodeInt[1..100]
API overview
The core definitions in Data.Binary.Typed are:
Typed(the main type)typed(constructTypedvalues)TypeFormat(a helper type fortyped)erase(deconstructTypedvales)
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 inTypeds)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:
encodeTyped(pack inTypedand thenencode)encodeTypedLike(usually much more efficient version ofencodeTyped)decodeTyped(decodeTypedByteStringto)EitherStringadecodeTypedOrFail(likedecodeTyped, but with more meta information)unsafeDecodeTyped(which throws a runtime error on type mismatch)