mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-06 03:05:28 -05:00
documentation for Bij
This commit is contained in:
parent
7e9b0e101b
commit
94f823fa4a
1 changed files with 91 additions and 8 deletions
99
bij.mli
99
bij.mli
|
|
@ -25,7 +25,95 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
(** {1 Bijective Serializer/Deserializer} *)
|
(** {1 Bijective Serializer/Deserializer} *)
|
||||||
|
|
||||||
type 'a t
|
(** This module helps writing serialization/deserialization code in
|
||||||
|
a type-safe way. It uses GADTs, and as such requires OCaml >= 4.00.1. *)
|
||||||
|
|
||||||
|
type 'a t = private
|
||||||
|
| Unit : unit t
|
||||||
|
| String : string t
|
||||||
|
| Int : int t
|
||||||
|
| Bool : bool t
|
||||||
|
| Float : float t
|
||||||
|
| List : 'a t -> 'a list t
|
||||||
|
| Many : 'a t -> 'a list t
|
||||||
|
| Opt : 'a t -> 'a option t
|
||||||
|
| Pair : 'a t * 'b t -> ('a * 'b) t
|
||||||
|
| Triple : 'a t * 'b t * 'c t -> ('a * 'b * 'c) t
|
||||||
|
| Quad : 'a t * 'b t * 'c t * 'd t -> ('a * 'b * 'c * 'd) t
|
||||||
|
| Quint : 'a t * 'b t * 'c t * 'd t * 'e t -> ('a * 'b * 'c * 'd * 'e) t
|
||||||
|
| Guard : ('a -> bool) * 'a t -> 'a t
|
||||||
|
| Map : ('a -> 'b) * ('b -> 'a) * 'b t -> 'a t
|
||||||
|
| Switch : ('a -> string * 'a inject_branch) *
|
||||||
|
(string-> 'a extract_branch) -> 'a t
|
||||||
|
and _ inject_branch =
|
||||||
|
| BranchTo : 'b t * 'b -> 'a inject_branch
|
||||||
|
and _ extract_branch =
|
||||||
|
| BranchFrom : 'b t * ('b -> 'a) -> 'a extract_branch
|
||||||
|
|
||||||
|
(** Conceptually, a value of type ['a t] describes the (persistent) structure
|
||||||
|
of the type ['a]. Combinators, listed in the next section (e.g., {!list_}
|
||||||
|
or {!pair}), are used to describe complicated structures from simpler
|
||||||
|
ones.
|
||||||
|
|
||||||
|
For instance, to serialize a [(int * string) list]:
|
||||||
|
|
||||||
|
{[let bij = Bij.(list_ (pair int_ string_));;
|
||||||
|
|
||||||
|
let l = [(1, "foo"); (2, "bar")];;
|
||||||
|
|
||||||
|
Bij.TrBencode.to_string ~bij l;;
|
||||||
|
- : string = "lli1e3:fooeli2e3:baree"
|
||||||
|
]}
|
||||||
|
|
||||||
|
Some types may not be directly describable, for instance records or
|
||||||
|
algebraic types. For those, more subtle combinators exist:
|
||||||
|
|
||||||
|
- [map] is a bijection between two types, and should be typically used to
|
||||||
|
map records to tuples (for which combinators exist)
|
||||||
|
|
||||||
|
- [switch] is a case disjunction. Each case can map to a different type,
|
||||||
|
thank to the power of GADT, and a {b key} needs to be provided for
|
||||||
|
each case, so that de-serialization can know which type to read.
|
||||||
|
|
||||||
|
- [fix] allows to describe recursive encodings. The user provides a function
|
||||||
|
which, given a ['a t lazy_t], builds a ['a t], and return its fixpoint.
|
||||||
|
|
||||||
|
For instance, let's take a simple symbolic expressions structure (can
|
||||||
|
be found in the corresponding test file "tests/test_bij.ml"):
|
||||||
|
|
||||||
|
{[
|
||||||
|
type term =
|
||||||
|
| Const of string
|
||||||
|
| Int of int
|
||||||
|
| App of term list;;
|
||||||
|
|
||||||
|
let bij_term =
|
||||||
|
Bij.(fix
|
||||||
|
(fun bij ->
|
||||||
|
switch
|
||||||
|
~inject:(function
|
||||||
|
| Const s -> "const", BranchTo (string_, s)
|
||||||
|
| Int i -> "int", BranchTo (int_, i)
|
||||||
|
| App l -> "app", BranchTo (list_ (Lazy.force bij), l))
|
||||||
|
~extract:(function
|
||||||
|
| "const" -> BranchFrom (string_, fun x -> Const x)
|
||||||
|
| "int" -> BranchFrom (int_, fun x -> Int x)
|
||||||
|
| "app" -> BranchFrom (list_ (Lazy.force bij), fun l -> App l)
|
||||||
|
| _ -> raise (DecodingError "unexpected case switch")))
|
||||||
|
)
|
||||||
|
]}
|
||||||
|
|
||||||
|
A bijection could be used for many things, but here our focus is on
|
||||||
|
serialization and de-serialization. The idea is that we can map a value
|
||||||
|
[x : 'a] to some general-purpose serialization format
|
||||||
|
(json, XML, B-encode, etc.) that we can then write to the disk or network;
|
||||||
|
the reverse operation is also possible (and bijectivity is enforced
|
||||||
|
by the fact that we use a single datatype ['a t] to describe both mappings).
|
||||||
|
|
||||||
|
For now, only a bijection to B-encode (see {!Bencode} and {!Bij.TrBencode})
|
||||||
|
is provided. The code is quite straightforward and could be extended
|
||||||
|
to XML or Json without hassle.
|
||||||
|
*)
|
||||||
|
|
||||||
(** {2 Bijection description} *)
|
(** {2 Bijection description} *)
|
||||||
|
|
||||||
|
|
@ -47,11 +135,6 @@ val guard : ('a -> bool) -> 'a t -> 'a t
|
||||||
|
|
||||||
val map : inject:('a -> 'b) -> extract:('b -> 'a) -> 'b t -> 'a t
|
val map : inject:('a -> 'b) -> extract:('b -> 'a) -> 'b t -> 'a t
|
||||||
|
|
||||||
type _ inject_branch =
|
|
||||||
| BranchTo : 'b t * 'b -> 'a inject_branch
|
|
||||||
type _ extract_branch =
|
|
||||||
| BranchFrom : 'b t * ('b -> 'a) -> 'a extract_branch
|
|
||||||
|
|
||||||
val switch : inject:('a -> string * 'a inject_branch) ->
|
val switch : inject:('a -> string * 'a inject_branch) ->
|
||||||
extract:(string -> 'a extract_branch) -> 'a t
|
extract:(string -> 'a extract_branch) -> 'a t
|
||||||
(** Discriminates unions based on the next character.
|
(** Discriminates unions based on the next character.
|
||||||
|
|
@ -59,12 +142,12 @@ val switch : inject:('a -> string * 'a inject_branch) ->
|
||||||
type (the argument of the algebraic constructor);
|
type (the argument of the algebraic constructor);
|
||||||
[extract] retrieves which type to parse based on the key. *)
|
[extract] retrieves which type to parse based on the key. *)
|
||||||
|
|
||||||
(** {2 Helpers} *)
|
|
||||||
|
|
||||||
val fix : ('a t lazy_t -> 'a t) -> 'a t
|
val fix : ('a t lazy_t -> 'a t) -> 'a t
|
||||||
(** Helper for recursive encodings. The parameter is the recursive bijection
|
(** Helper for recursive encodings. The parameter is the recursive bijection
|
||||||
itself. It must be lazy. *)
|
itself. It must be lazy. *)
|
||||||
|
|
||||||
|
(** {2 Helpers} *)
|
||||||
|
|
||||||
val with_version : string -> 'a t -> 'a t
|
val with_version : string -> 'a t -> 'a t
|
||||||
(** Guards the values with a given version. Only values encoded with
|
(** Guards the values with a given version. Only values encoded with
|
||||||
the same version will fit. *)
|
the same version will fit. *)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue