Types
This information is **very low-level **and may be difficult for newcomers to grasp. Feel free to revisit it later if needed.
This section analyzes complex and unconventional typed language binary (TL-B) structures. We recommend reading this documentation to familiarize yourself with the topic and get started.
Either
left$0 {X:Type} {Y:Type} value:X = Either X Y;
right$1 {X:Type} {Y:Type} value:Y = Either X Y;
The either type is used when one of two possible result types may be present. The choice of type depends on the prefix bit: if the prefix bit is 0, the left type is serialized; if it is 1, the right type is serialized.
This construct is used, for example, when serializing messages—where the body is either included directly in the main cell or stored in a separate referenced cell.
Maybe
nothing$0 {X:Type} = Maybe X;
just$1 {X:Type} value:X = Maybe X;
The maybe type is used to represent optional values. In this case, the first bit indicates a value: if the bit is 0, the value is not serialized and skipped; if the bit is 1, the value follows and is serialized.
Both
pair$_ {X:Type} {Y:Type} first:X second:Y = Both X Y;
The both type variation is used exclusively with regular pairs, where both types are serialized sequentially without any conditions.
Unary
The unary functional type is commonly used for dynamic sizing in structures such as hml_short.
Unary supports two main options:
unary_zero$0 = Unary ~0;
unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1);
Unary serialization
The unary_zero variation is straightforward: if the first bit is 0, the result of the entire unary deserialization is 0.
The unary_succ variation, however, is more complex—it is loaded recursively and represents a value of ~(n + 1). This means it repeatedly calls itself until it reaches unary_zero. In other words, the desired value will equal the number of units in a row.
For example, consider the serialization of the bitstring 110.
The deserialization call chain is as follows:
unary_succ$1 -> unary_succ$1 -> unary_zero$0
Once unary_zero is reached, the value is returned up the call stack, similar to how values are returned in a recursive function.
To better visualize the result, let's trace the return path:
0 -> ~(0 + 1) -> ~(1 + 1) -> 2
This shows that the bitstring 110 corresponds to Unary 2.
Unary deserialization
Suppose we have a Foo type defined as follows:
foo$_ u:(Unary 2) = Foo;
Based on the explanation above, Foo is deserialized as:

foo u:(unary_succ x:(unary_succ x:(unary_zero)))
Hashmap
The Hashmap complex type is used to store dictionaries from FunC smart contract code, i.e., dict.
The following TL-B structures are used to serialize a Hashmap with a fixed key length:
hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n)
{n = (~m) + l} node:(HashmapNode m X) = Hashmap n X;
hmn_leaf#_ {X:Type} value:X = HashmapNode 0 X;
hmn_fork#_ {n:#} {X:Type} left:^(Hashmap n X)
right:^(Hashmap n X) = HashmapNode (n + 1) X;
hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m;
hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m;
hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m;
unary_zero$0 = Unary ~0;
unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1);
hme_empty$0 {n:#} {X:Type} = HashmapE n X;
hme_root$1 {n:#} {X:Type} root:^(Hashmap n X) = HashmapE n X;
This means the root structure uses HashmapE and one of its two possible states: either hme_empty or hme_root.