Safe Haskell | Safe-Inferred |
---|---|

Language | Haskell2010 |

Cardano.Ledger.Binary.Coders provides tools for writing `EncCBOR`

and `DecCBOR`

instances (see module `Binary`

) in an intuitive way that mirrors the way one
constructs values of a particular type. Advantages include:

- Book-keeping details neccesary to write correct instances are hidden from the user.
- Inverse
`EncCBOR`

and`DecCBOR`

instances have visually similar definitions. - Advanced instances involving sparse-encoding, compact-representation, and
`Annotator`

instances are also supported.

A Guide to Visual inspection of Duality in Encode and Decode

`(Sum c)`

and`(SumD c)`

are duals`(Rec c)`

and`(RecD c)`

are duals`(Keyed c)`

and`(KeyedD c)`

are duals`(OmitC x)`

and`(Emit x)`

are duals`(Omit p ..)`

and`(Emit x)`

are duals if (p x) is True`(To x)`

and`(From)`

are duals if (x::T) and (forall (y::T). isRight (roundTrip y))`(E enc x)`

and`(D dec)`

are duals if (forall x . isRight (roundTrip' enc dec x))`(f !> x)`

and`(g <! y)`

are duals if (f and g are duals) and (x and y are duals)

Duality properties of `(Summands name decodeT)`

and ```
(SparseKeyed name (init::T) pick
required)
```

also exist but are harder to describe succinctly.

## Synopsis

- data Encode (w ∷ Wrapped) t where
- Rec ∷ t → Encode ('Closed 'Dense) t
- Sum ∷ t → Word → Encode 'Open t
- Keyed ∷ t → Encode ('Closed 'Sparse) t
- To ∷ EncCBOR a ⇒ a → Encode ('Closed 'Dense) a
- E ∷ (t → Encoding) → t → Encode ('Closed 'Dense) t
- MapE ∷ (a → b) → Encode w a → Encode w b
- OmitC ∷ t → Encode w t
- Tag ∷ Word → Encode ('Closed x) t → Encode ('Closed x) t
- Omit ∷ (t → Bool) → Encode ('Closed 'Sparse) t → Encode ('Closed 'Sparse) t
- Key ∷ Word → Encode ('Closed 'Dense) t → Encode ('Closed 'Sparse) t
- ApplyE ∷ Encode w (a → t) → Encode ('Closed r) a → Encode w t

- (!>) ∷ Encode w (a → t) → Encode ('Closed r) a → Encode w t
- encode ∷ Encode w t → Encoding
- runE ∷ Encode w t → t
- encodeDual ∷ ∀ t. (EncCBOR t, DecCBOR t) ⇒ t → Encode ('Closed 'Dense) t
- encodeKeyedStrictMaybeWith ∷ Word → (a → Encoding) → StrictMaybe a → Encode ('Closed 'Sparse) (StrictMaybe a)
- encodeKeyedStrictMaybe ∷ EncCBOR a ⇒ Word → StrictMaybe a → Encode ('Closed 'Sparse) (StrictMaybe a)
- 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 encoders.

data Encode (w ∷ Wrapped) t where Source #

A first-order domain specific langage for describing EncCBOR instances. Applying
the interpreter `encode`

to a well-typed `(Encode w T)`

always produces a valid encoding for `T`

.
Constructing an Encode of type T is just like building a value of type T, applying a constructor
of `T`

to the correctly typed arguments. For example

data T = T Bool Word instance EncCBOR T where encCBOR (T b w) = encode (Rec T !> To b !> To w)

Note the similarity of

`(`

and *T* *b* *w*)`(`

and *T* $ *b* $ *w*)`(Rec `

*T* !> To *b* !> To *w*)

Where (`!>`

) is the infx version of `ApplyE`

with the same infixity and precedence as (`$`

). Note
how the constructor and each (component, field, argument) is labeled with one of the constructors
of `Encode`

, and are combined with the application operator (`!>`

). Using different constructors supports
different styles of encoding.

Rec ∷ t → Encode ('Closed 'Dense) t | Label the constructor of a Record-like datatype (one with exactly 1 constructor) as an Encode. |

Sum ∷ t → Word → Encode 'Open t | Label one of the constructors of a sum datatype (one with multiple constructors) as an Encode |

Keyed ∷ t → Encode ('Closed 'Sparse) t | Label the constructor of a Record-like datatype as being encoded sparsely (storing only non-default values). |

To ∷ EncCBOR a ⇒ a → Encode ('Closed 'Dense) a | Label an (component, field, argument) to be encoded using an existing EncCBOR instance. |

E ∷ (t → Encoding) → t → Encode ('Closed 'Dense) t | Label an (component, field, argument) to be encoded using an existing EncCBOR instance. |

MapE ∷ (a → b) → Encode w a → Encode w b | Lift one Encode to another with a different type. Used to make a Functor instance of (Encode w). |

OmitC ∷ t → Encode w t | Skip over the (component,field, argument), don't encode it at all (used in sparse encoding). |

Tag ∷ Word → Encode ('Closed x) t → Encode ('Closed x) t | Precede the given encoding (in the produced bytes) with the given tag Word. |

Omit ∷ (t → Bool) → Encode ('Closed 'Sparse) t → Encode ('Closed 'Sparse) t | Omit the (component,field, argument) if the function is True, otherwise encode with the given encoding. |

Key ∷ Word → Encode ('Closed 'Dense) t → Encode ('Closed 'Sparse) t | Precede the encoding (in the produced bytes) with the key Word. Analagous to |

ApplyE ∷ Encode w (a → t) → Encode ('Closed r) a → Encode w t | Apply a functional encoding (arising from |

(!>) ∷ Encode w (a → t) → Encode ('Closed r) a → Encode w t infixl 4 Source #

Infix operator version of `ApplyE`

. Has the same infxity and operator precedence as `$`

encode ∷ Encode w t → Encoding Source #

Translate a first-order @(Encode w d) domain specific langage program, into an `Encoding`

.

# Index types for well-formed Coders.

encodeDual ∷ ∀ t. (EncCBOR t, DecCBOR t) ⇒ t → Encode ('Closed 'Dense) t Source #

Use `encodeDual`

and `decodeDual`

, when you want to
guarantee that a type has both `EncCBOR`

and `FromCBR`

instances.

# Containers, Combinators, Annotators

encodeKeyedStrictMaybeWith ∷ Word → (a → Encoding) → StrictMaybe a → Encode ('Closed 'Sparse) (StrictMaybe a) Source #

encodeKeyedStrictMaybe ∷ EncCBOR a ⇒ Word → StrictMaybe a → Encode ('Closed 'Sparse) (StrictMaybe a) Source #

# 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.