Copyright | (c) Justin Le 2015 |
---|---|
License | MIT |
Maintainer | justin@jle.im |
Stability | unstable |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
Provides tools for serializing and decoding data into ByteString
tagged with information about its type. Really, most of this should be
used by libraries and frameworks and abstracted over. Typical use cases
are the polymorphic communication channels in distributed computing used
by Cloud Haskell and distributed-process --- data of any type can come
through the channel, and the framework can chose to ignore, queue, or
accept data depending on the type the data is tagged with. Designed to
work with cross-platform GHC backends like ghcjs.
When decoding data, the result is polymorphic, and you should either allow GHC to infer what you want somehow somewhere, or specify it explicitly.
Quick example:
> let x = encodeTagged (1 :: Int) > decodeTagged x :: Maybe Bool Nothing > decodeTagged x :: Maybe Int Just 1
The interface is very similar to that of Data.Dynamic.
Also provided here is the internal TagFingerprint
data type, so that
you can categorize, sort, and queue Tagged
or ByteString
based on
the types they represent.
It might be significant to note that the current TagFingerprint
implementation is a little shaky; it's a bit tricky getting all GHC
platforms to agree on a meaningful TypeRep
serialization, and we will
have a better implementation eventually. For now, it just uses an MD5
hash of the string name of the type. So for now, don't encode/decode
things with the same type name but exist in different modules
(Data.Text.Text or Data.Text.Lazy.Text, for example) through the
same polymorphic channel! This is a bit limiting, admittedly, but until
I or the backend maintainers find out a way to ensure that type
fingerprints match up per backend, be aware of this limitation.
- encodeTagged :: (Binary a, Typeable a) => a -> ByteString
- decodeTagged :: (Binary a, Typeable a) => ByteString -> Maybe a
- bsFingerprint :: ByteString -> Maybe TagFingerprint
- data Tagged a
- data TagFingerprint
- typeFingerprint :: Typeable a => a -> TagFingerprint
Encoding and decoding tagged data
encodeTagged :: (Binary a, Typeable a) => a -> ByteString Source #
Encode data into a ByteString
with its type data tagged.
Remember that for now, types are distinguished by their string names, so two types of the same name in different modules will not have unique tags.
decodeTagged :: (Binary a, Typeable a) => ByteString -> Maybe a Source #
Decode tagged data from a ByteString
. The return type is
polymorphic, so it'll attempt to decode it by inferred or specified
type.
- If the data is not decoded,
Nothing
is returned. - If successfully decoded data is tagged with a
Fingerprint
not matching the desired type,Nothing
is also returned. - If the data is successfully decoded *and* the tagged
Fingerprint
matches the desired type,Just x
is returned, wherex
is the originally encoded data (with its tag stripped).
bsFingerprint :: ByteString -> Maybe TagFingerprint Source #
With a ByteString
, expecting tagged data, returns the Fingerprint
that the data is tagged with. Returns Nothing
if the data is not
decodable as tagged data. Might accidentally decode untagged data
though!
Manipulating
A data type tupling together data with a TagFingerprint
,
representing data tagged with its type.
It's best to interface directly with data using encodeTagged
,
decodeTagged
, etc, using tag
to tag data and extractTagged
to
extract data from valid tagged data. This type is exported mostly when
you want to specifically decode a ByteString
into tagged data, and
manually extract it yourself. If you are writing a framework, it is
preferred to handle this for the end user.
data TagFingerprint Source #
A data type representing a fingerprint for a Typeable
type.
Ideally, this would be Internal'
s own Fingerprint
types; however, for some reason, the fingerprints for the same data type
from the same modules differ between different GHC backends. So for
now, it is just a ByteString
representation of the name of the type.
This is literally a bad idea, and so two types with the same name but
from different modules will share a non-unique TagFingerprint
.
Hopefully in the future when I find out a way to fix this or the GHC
backend maintainers find a way to provide consistent type fingerprints,
this will be fixed.
This type is mostly used for the ability to categorized Tagged items by their type.
emptyTagFP
gives a TagFingerprint
that will most likely never be
matched by any actual tag from a real type, so can be used as a test if
needed. This replaces functionality that used to come from the
Default
instance.
typeFingerprint :: Typeable a => a -> TagFingerprint Source #
Compute the Fingerprint
representing a type. It is non-strict on
its parameter, so passing in undefined should work if you want to just
get the Fingerprint
of a specific type without having data of that
type on hand:
typeFingerprint (undefined :: Int)