Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
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 ::Int
enc =encode
val dec =decode
enc ::Bool
in
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
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
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.
-- ComputesInt
s 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
(typed
Full
(0 ::Int
)) manyIntsCached = mapencodeInt
[1..100]
API overview
The core definitions in Data.Binary.Typed are:
Typed
(the main type)typed
(constructTyped
values)TypeFormat
(a helper type fortyped
)erase
(deconstructTyped
vales)
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 inTyped
s)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 inTyped
and thenencode
)encodeTypedLike
(usually much more efficient version ofencodeTyped
)decodeTyped
(decodeTyped
ByteString
to
)Either
String
adecodeTypedOrFail
(likedecodeTyped
, but with more meta information)unsafeDecodeTyped
(which throws a runtime error on type mismatch)