Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
Synopsis
- data Decode (w ∷ Wrapped) t where
- RecD ∷ t → Decode ('Closed 'Dense) t
- SumD ∷ t → Decode 'Open t
- Summands ∷ Text → (Word → Decode 'Open t) → Decode ('Closed 'Dense) t
- SparseKeyed ∷ Typeable t ⇒ String → t → (Word → Field t) → [(Word, String)] → Decode ('Closed 'Dense) t
- KeyedD ∷ t → Decode ('Closed 'Sparse) t
- From ∷ DecCBOR t ⇒ Decode w t
- D ∷ (∀ s. Decoder s t) → Decode ('Closed 'Dense) t
- ApplyD ∷ Decode w1 (a → t) → Decode ('Closed d) a → Decode w1 t
- Invalid ∷ Word → Decode w t
- Map ∷ (a → b) → Decode w a → Decode w b
- TagD ∷ Word → Decode ('Closed x) t → Decode ('Closed x) t
- Emit ∷ t → Decode w t
- Ann ∷ Decode w t → Decode w (Annotator t)
- ApplyAnn ∷ Decode w1 (Annotator (a → t)) → Decode ('Closed d) (Annotator a) → Decode w1 (Annotator t)
- ApplyErr ∷ Decode w1 (a → Either String t) → Decode ('Closed d) a → Decode w1 t
- (<!) ∷ Decode w1 (a → t) → Decode ('Closed w) a → Decode w1 t
- (<*!) ∷ Decode w1 (Annotator (a → t)) → Decode ('Closed d) (Annotator a) → Decode w1 (Annotator t)
- (<?) ∷ Decode w1 (a → Either String t) → Decode ('Closed d) a → Decode w1 t
- decode ∷ Decode w t → Decoder s t
- decodeSparse ∷ Typeable a ⇒ String → a → (Word → Field a) → [(Word, String)] → Decoder s a
- data Density
- data Wrapped where
- data Field t where
- ofield ∷ (StrictMaybe x → t → t) → Decode ('Closed d) x → Field t
- invalidField ∷ ∀ t. Word → Field t
- field ∷ (x → t → t) → Decode ('Closed d) x → Field t
- fieldGuarded ∷ String → (x → Bool) → (x → t → t) → Decode ('Closed d) x → Field t
- fieldA ∷ Applicative ann ⇒ (x → t → t) → Decode ('Closed d) x → Field (ann t)
- fieldAA ∷ Applicative ann ⇒ (x → t → t) → Decode ('Closed d) (ann x) → Field (ann t)
- decodeDual ∷ ∀ t. (EncCBOR t, DecCBOR t) ⇒ Decode ('Closed 'Dense) t
- listDecodeA ∷ Decode ('Closed 'Dense) (Annotator x) → Decode ('Closed 'Dense) (Annotator [x])
- mapDecodeA ∷ Ord k ⇒ Decode ('Closed 'Dense) (Annotator k) → Decode ('Closed 'Dense) (Annotator v) → Decode ('Closed 'Dense) (Annotator (Map k v))
- setDecodeA ∷ Ord x ⇒ Decode ('Closed 'Dense) (Annotator x) → Decode ('Closed 'Dense) (Annotator (Set x))
- decodeRecordNamed ∷ Text → (a → Int) → Decoder s a → Decoder s a
- decodeRecordNamedT ∷ (MonadTrans m, Monad (m (Decoder s))) ⇒ Text → (a → Int) → m (Decoder s) a → m (Decoder s) a
- decodeRecordSum ∷ Text → (Word → Decoder s (Int, a)) → Decoder s a
- invalidKey ∷ MonadFail m ⇒ Word → m a
- unusedRequiredKeys ∷ Set Word → [(Word, String)] → String → Decoder s a
- duplicateKey ∷ String → Word → Decoder s a
- guardUntilAtLeast ∷ DecCBOR a ⇒ String → Version → Decode ('Closed 'Dense) a
Creating decoders.
data Decode (w ∷ Wrapped) t where Source #
The type (
is designed to be dual to Decode
t)(
. It was designed so that
in many cases a decoder can be extracted from an encoder by visual inspection. We now give some
example of Encode
t)(Decode t)
and (Encode t)
pairs.
An example with 1 constructor (a record) uses Rec
and RecD
In this example, let Int
and C
have EncCBOR
instances.
data C = C { unC :: Text.Text } instance EncCBOR C where encCBOR (C t) = encCBOR t instance DecCBOR C where decCBOR = C $ decCBOR data B = B { unB :: Text.Text } data A = ACon Int B C encodeA :: A -> Encode ('Closed 'Dense) A encodeA (ACon i b c) = Rec ACon !> To i !> E (encCBOR . unB) b !> To c decodeA :: Decode ('Closed 'Dense) A decodeA = RecD ACon From <! D (B <$ decCBOR) <! From instance EncCBOR A where encCBOR = encode . encodeA instance DecCBOR A where decCBOR = decode decodeA
An example with multiple constructors uses Sum
, SumD
, and Summands
.
data N = N1 Int | N2 B Bool | N3 A encodeN :: N -> Encode 'Open N encodeN (N1 i) = Sum N1 0 !> To i encodeN (N2 b tf) = Sum N2 1 !> E (encCBOR . unB) b !> To tf encodeN (N3 a) = Sum N3 2 !> To a decodeN :: Decode ('Closed 'Dense) N -- Note each clause has an 'Open decoder, decodeN = Summands N decodeNx -- But Summands returns a ('Closed 'Dense) decoder where decodeNx 0 = SumD N1 <! From decodeNx 1 = SumD N2 D (B <$ decCBOR) <! From decodeNx 3 = SumD N3 <! From decodeNx k = Invalid k instance EncCBOR N where encCBOR x = encode(encodeN x) instance DecCBOR N where decCBOR = decode decodeN
Two examples using variants of sparse encoding for records, i.e. those datatypes with only one constructor.
The Virtual constructor approach using Summands
, OmitC
, Emit
.
The Sparse field approach using Keyed
, Key
and Omit
. The approaches work
because encoders and decoders don't put
fields with default values in the Encoding, and reconstruct the default values on the decoding side.
We will illustrate the two approaches using the datatype M
data M = M Int [Bool] Text.Text deriving (Show, Typeable) a0, a1, a2, a3 :: M -- Some illustrative examples, using things that might be given default values. a0 = M 0 [] ABC a1 = M 0 [True] ABC a2 = M 9 [] ABC a3 = M 9 [False] ABC
The virtual constructor strategy pretends there are mutiple constructors
Even though there is only one. We use invariants about the data to avoid
encoding some of the values. Note the use of Sum
with virtual constructor tags 0,1,2,3
encM :: M -> Encode 'Open M
encM (M 0 [] t) = Sum M 0 !> OmitC 0 !> OmitC [] !> To t
encM (M 0 bs t) = Sum M 1 !> OmitC 0 !> To bs !> To t
encM (M n [] t) = Sum M 2 !> To n !> OmitC [] !> To t
encM (M n bs t) = Sum M 3 !> To n !> To bs !> To t
decM :: Word -> Decode 'Open M
decM 0 = SumD M <! Emit 0 <! Emit [] <! From -- The virtual constructors tell which fields have been Omited
decM 1 = SumD M <! Emit 0 <! From <! From -- So those fields are reconstructed using Emit
.
decM 2 = SumD M <! From <! Emit [] <! From
decM 3 = SumD M <! From <! From <! From
decM n = Invalid n
instance EncCBOR M where
encCBOR m = encode (encM m)
instance DecCBOR M where
decCBOR = decode (Summands M decM)
The Sparse field approach uses N keys, one for each field that is not defaulted. For example
(M 9 [True] (pack "hi")))
. Here zero fields are defaulted, so there should be 3 keys.
Encoding this example would look something like this.
[TkMapLen 3,TkInt 0,TkInt 9,TkInt 1,TkListBegin,TkBool True,TkBreak,TkInt 2,TkString "hi"] ^key ^key ^key
So the user supplies a function, that encodes every field, each field must use a unique
key, and fields with default values have Omit wrapped around the Key encoding.
The user must ensure that there is NOT an Omit on a required field. encM2
is an example.
encM2:: M -> Encode ('Closed 'Sparse) M encM2 (M n xs t) = Keyed M !> Omit (== 0) (Key 0 (To n)) -- Omit if n is zero !> Omit null (Key 1 (To xs)) -- Omit if xs is null !> Key 2 (To t) -- Always encode t
To write an Decoder we must pair a decoder for each field, with a function that updates only
that field. We use the Field
GADT to construct these pairs, and we must write a function, that
for each field tag, picks out the correct pair. If the Encode and Decode don't agree on how the
tags correspond to a particular field, things will fail.
boxM :: Word -> Field M boxM 0 = field update0 From where update0 n (M _ xs t) = M n xs t boxM 1 = field update1 From where update1 xs (M n _ t) = M n xs t boxM 2 = field update2 From where update2 t (M n xs _) = M n xs t boxM n = invalidField n
Finally there is a new constructor for Decode
, called SparseKeyed
, that decodes field
keyed sparse objects. The user supplies an initial value and field function, and a list
of tags of the required fields. The initial value should have default values and
any well type value in required fields. If the encode function (baz above) is
encoded properly the required fields in the initial value should always be over
overwritten. If it is not written properly, or a bad encoding comes from somewhere
else, the intial values in the required fields might survive decoding. The list
of required fields is checked.
instance DecCBOR M where decCBOR = decode (SparseKeyed TT -- ^ Name of the type being decoded (M 0 [] (Text.pack "a")) -- ^ The default value boxM -- ^ The Field function [(2, Stringpart)] -- ^ The required Fields ) instance EncCBOR M where encCBOR m = encode(encM2 m)
RecD ∷ t → Decode ('Closed 'Dense) t | Label the constructor of a Record-like datatype (one with exactly 1 constructor) as a Decode. |
SumD ∷ t → Decode 'Open t | Label the constructor of a Record-like datatype (one with multiple constructors) as an Decode. |
Summands ∷ Text → (Word → Decode 'Open t) → Decode ('Closed 'Dense) t | Lift a Word to Decode function into a DeCode for a type with multiple constructors. |
SparseKeyed | Lift a Word to Field function into a DeCode for a type with 1 constructor stored sparsely |
KeyedD ∷ t → Decode ('Closed 'Sparse) t | Label a (component, field, argument) as sparsely stored, which will be populated with the default value. |
From ∷ DecCBOR t ⇒ Decode w t | Label a (component, field, argument). It will be decoded using the existing
DecCBOR instance at |
D ∷ (∀ s. Decoder s t) → Decode ('Closed 'Dense) t | Label a (component, field, argument). It will be decoded using the given decoder. |
ApplyD ∷ Decode w1 (a → t) → Decode ('Closed d) a → Decode w1 t | Apply a functional decoding (arising from |
Invalid ∷ Word → Decode w t | Mark a Word as a Decoding which is not a valid Decoding. Used when decoding sums that are tagged out of range. |
Map ∷ (a → b) → Decode w a → Decode w b | Used to make (Decode w) an instance of Functor. |
TagD ∷ Word → Decode ('Closed x) t → Decode ('Closed x) t | Assert that the next thing decoded must be tagged with the given word. |
Emit ∷ t → Decode w t | Decode the next thing, not by inspecting the bytes, but pulled out of thin air,
returning |
Ann ∷ Decode w t → Decode w (Annotator t) | Lift a |
ApplyAnn | Apply a functional decoding (arising from |
ApplyErr ∷ Decode w1 (a → Either String t) → Decode ('Closed d) a → Decode w1 t | the function to Either can raise an error when applied by returning (Left errorMessage) |
Instances
Applicative (Decode ('Closed d)) Source # | |
Defined in Cardano.Ledger.Binary.Decoding.Coders pure ∷ a → Decode ('Closed d) a Source # (<*>) ∷ Decode ('Closed d) (a → b) → Decode ('Closed d) a → Decode ('Closed d) b Source # liftA2 ∷ (a → b → c) → Decode ('Closed d) a → Decode ('Closed d) b → Decode ('Closed d) c Source # (*>) ∷ Decode ('Closed d) a → Decode ('Closed d) b → Decode ('Closed d) b Source # (<*) ∷ Decode ('Closed d) a → Decode ('Closed d) b → Decode ('Closed d) a Source # | |
Functor (Decode w) Source # | |
(<!) ∷ Decode w1 (a → t) → Decode ('Closed w) a → Decode w1 t infixl 4 Source #
Infix form of ApplyD
with the same infixity and precedence as ($)
.
(<*!) ∷ Decode w1 (Annotator (a → t)) → Decode ('Closed d) (Annotator a) → Decode w1 (Annotator t) infixl 4 Source #
Infix form of ApplyAnn
with the same infixity and precedence as ($)
.
(<?) ∷ Decode w1 (a → Either String t) → Decode ('Closed d) a → Decode w1 t infixl 4 Source #
Infix form of ApplyErr
with the same infixity and precedence as ($)
.
Index types for well-formed Coders.
Some CBOR instances wrap encoding sequences with prefixes and suffixes. I.e.
prefix , encode, encode, encode , ... , suffix.
There are two kinds of wrapping coders: Nary sums, and Sparsely encoded products.
Coders in these classes can only be decoded when they are wrapped by their
closing forms Summands
and SparseKeyed
. Another dimension, where we use indexes
to maintain type safety, are records which can be
encoded densely (all their fields serialised) or sparsely (only some of their
fields). We use indexes to types to try and mark (and enforce) these distinctions.
Index for record density. Distinguishing (all the fields) from (some of the fields).
Index for a wrapped Coder. Wrapping is necessary for Summands
and SparseKeyed
.
A Field pairs an update function and a decoder for one field of a Sparse record.
invalidField ∷ ∀ t. Word → Field t Source #
fieldA ∷ Applicative ann ⇒ (x → t → t) → Decode ('Closed d) x → Field (ann t) Source #
Sparse decode something with a (DecCBOR (Annotator t)) instance
A special case of field
fieldAA ∷ Applicative ann ⇒ (x → t → t) → Decode ('Closed d) (ann x) → Field (ann t) Source #
Sparse decode something with a (DecCBOR (Annotator t)) instance
Using Duals
decodeDual ∷ ∀ t. (EncCBOR t, DecCBOR t) ⇒ Decode ('Closed 'Dense) t Source #
Use encodeDual
and decodeDual
, when you want to
guarantee that a type has both EncCBOR
and FromCBR
instances.
Containers, Combinators
listDecodeA ∷ Decode ('Closed 'Dense) (Annotator x) → Decode ('Closed 'Dense) (Annotator [x]) Source #
mapDecodeA ∷ Ord k ⇒ Decode ('Closed 'Dense) (Annotator k) → Decode ('Closed 'Dense) (Annotator v) → Decode ('Closed 'Dense) (Annotator (Map k v)) Source #
setDecodeA ∷ Ord x ⇒ Decode ('Closed 'Dense) (Annotator x) → Decode ('Closed 'Dense) (Annotator (Set x)) Source #
Low level (Encoding/Decoder) utility functions
decodeRecordNamedT ∷ (MonadTrans m, Monad (m (Decoder s))) ⇒ Text → (a → Int) → m (Decoder s) a → m (Decoder s) a Source #
invalidKey ∷ MonadFail m ⇒ Word → m a Source #
Report an error when a numeric key of the type constructor doesn't match.