This library contains the definitions of the types which are used to transmit values "over-the-wire" (over the network, between different processes, ...). It does not declare or implement operations on these types, to keep the library as lightweight as possible.
The intended goal of this library is to be a light dependency for other parts of the code base which might need to manipulate values of those types without having to link to heavy modules defining operations on them (especially for code which transpiles to JavaScript).
This library is organized as a mirror of the codebase modules implementing actually operations on the types: to each type X.Y.t
in the codebase corresponds a type Mina_wire_types.X.Y.t
in this library. This includes versioned types, with a notable exception: the `Stable` module layer is always omitted in this library.
For example, the type Currency
.Amount.Stable.V1.t is equivalent to Mina_wire_types.Currency.Amount.V1.t
.
Since types defined here and types defined in actual implementation modules are intended to be used interchangeably, modules implementing operations on these types have to take a few precautions to ensure the type system can unify both. These precautions depend on if the type is intended to be public, or hidden (private/abstract).
If a type has a concrete definition in the corresponding signature of this library, then any module wishing to re-define it simply has to add a type equality to its re-definition, to ensure compatibility:
mina_wire_types/foo.mli
type t = Bar of int
foo/foo.ml
type t = Mina_wire_types.Foo.t = Bar of int
let add (Bar a) (Bar b) = Bar (a + b)
This only has to be done for types that are records or variants: simple type aliases do not need (and in fact cannot have) another type constraint.
It is a bit more complicated for types which are meant to be hidden from the code base (either abstract or private types): we have to use some machinery to "reveal" the type definition to the module meant to implement operations on that type.
To that end, each module Mina_wire_types.M
defining a hidden type `t` has a corresponding Mina_wire_types.M.Make
functor to reveal the type, to be used only by the actual implementation of M.t
.
For example, the types in Currency
can be implemented by using Mina_wire_types.Currency.Make
. Every Make
functor has two arguments:
Make_sig
: a functor constructing the expected signature of the final implementation module, from the abstract definition of t
,Make_str
: a functor implementing that module from the concrete definition of t
. The result must have the signature declared by the first argumentThe result is the full implementation module with the correct, compatible type, which can then be included.
To get started with a practical example, see the implementation of Currency
in lib/currency/currency.ml(i), which follows this pattern. See also the corresponding currency.ml(i) definitions in this library, which fully document the pattern used everywhere in this library.
To ensure this library and the codebase are consistent and can be used interchangeably, a test module in `test/type_equalities.ml` asserts that every main type of this library can be unified with its corresponding implementation type by the compiler.
It's important to add these tests, as an inconsistency not caught by these tests can cause head-scratching errors in random places of the code base.