Legend:
Library
Module
Module type
Parameter
Class
Class type
Dynamic types for Irmin values.
Yet-an-other type combinator library
Type provides type combinators to define runtime representation for OCaml types and generic operations to manipulate values with a runtime type representation.
The type combinators supports all the usual type primitives but also compact definitions of records and variants. It also allows the definition of run-time representations of recursive types.
The type for representing open records of type 'a with a constructor of type 'b. 'c represents the remaining fields to be described using the (|+) operator. An open record initially satisfies 'c = 'b and can be sealed once 'c = 'a.
record n f is an incomplete representation of the record called n of type 'a with constructor f. To complete the representation, add fields with (|+) and then seal the record with sealr.
sealr r seals the open record r. Raises.Invalid_argument if two or more fields share the same name.
Putting all together:
type menu = { restaurant : string; items : (string * int32) list }
let t =
record "t" (fun restaurant items -> { restaurant; items })
|+ field "restaurant" string (fun t -> t.restaurant)
|+ field "items" (list (pair string int32)) (fun t -> t.items)
|> sealr
The type for representing open variants of type 'a with pattern matching of type 'b. 'c represents the remaining constructors to be described using the (|~) operator. An open variant initially satisfies c' = 'b and can be sealed once 'c = 'a.
val variant : string ->'b->('a, 'b, 'b)open_variant
variant n p is an incomplete representation of the variant type called n of type 'a using p to deconstruct values. To complete the representation, add cases with (|~) and then seal the variant with sealv.
sealv v seals the open variant v. Raises.Invalid_argument if two or more cases of same arity share the same name.
Putting all together:
type t = Foo | Bar of string
let t =
variant "t" (fun foo bar -> function Foo -> foo | Bar s -> bar s)
|~ case0 "Foo" Foo
|~ case1 "Bar" string (fun x -> Bar x)
|> sealv
enum n cs is a representation of the variant type called n with singleton cases cs. e.g.
type t = Foo | Bar | Toto
let t = enum "t" [ ("Foo", Foo); ("Bar", Bar); ("Toto", Toto) ]
Raises.Invalid_argument if two or more cases share the same name.
Recursive definitions
Type allows a limited description of recursive records and variants.
TODO: describe the limitations, e.g. only regular recursion and no use of the generics inside the mu* functions and the usual caveats with recursive values (such as infinite loops on most of the generics which don't check sharing).
mu2 f is the representations r and s such that r, s = mu2 r s.
For instance:
type r = { foo : int; bar : string list; z : z option }
and z = { x : int; r : r list }
(* Build the representation of [r] knowing [z]'s. *)
let mkr z =
record "r" (fun foo bar z -> { foo; bar; z })
|+ field "foo" int (fun t -> t.foo)
|+ field "bar" (list string) (fun t -> t.bar)
|+ field "z" (option z) (fun t -> t.z)
|> sealr
(* And the representation of [z] knowing [r]'s. *)
let mkz r =
record "z" (fun x r -> { x; r })
|+ field "x" int (fun t -> t.x)
|+ field "r" (list r) (fun t -> t.r)
|> sealr
(* Tie the loop. *)
let r, z = mu2 (fun r z -> (mkr z, mkz y))
Generic Operations
Given a value 'a t, it is possible to define generic operations on value of type 'a such as pretty-printing, parsing and unparsing.
Similar to dump but pretty-prints the JSON representation instead of the OCaml one. See encode_json for details about the encoding.
For instance:
type t = { foo : int option; bar : string list }
let t =
record "r" (fun foo bar -> { foo; bar })
|+ field "foo" (option int) (fun t -> t.foo)
|+ field "bar" (list string) (fun t -> t.bar)
|> sealr
let s = Fmt.strf "%a\n" (pp t) { foo = None; bar = [ "foo" ] }
(* s is "{ foo = None; bar = [\"foo\"]; }" *)
let j = Fmt.strf "%a\n" (pp_json t) { foo = None; bar = [ "foo" ] }
(* j is "{ \"bar\":[\"foo\"] }" *)
NOTE: this will automatically convert JSON fragments to valid JSON objects by adding an enclosing array if necessary.
encode_json t e encodes t into the jsonm encoder e. The encoding is a relatively straightforward translation of the OCaml structure into JSON. The main highlights are:
The unit value () is translated into the empty object {}.
OCaml ints are translated into JSON floats.
OCaml strings are translated into JSON strings. You must then ensure that the OCaml strings contains only valid UTF-8 characters.
OCaml options are translated differently depending on context: record fields with a value of None are removed from the JSON object; record fields with a value of Some x are automatically unboxed into x; and outside of records, None is translated into null and Some x into {"some": x'} with x' the JSON encoding of x.
Variant cases built using case0 are represented as strings.
Variant cases built using case1 are represented as a record with one field; the field name is the name of the variant.
NOTE: this can be used to encode JSON fragments. It's the responsibility of the caller to ensure that the encoded JSON fragment fits properly into a well-formed JSON object.
pre_hash t x is the string representation of x, of type t, which will be used to compute the digest of the value. By default it's to_bin_string t x but it can be overriden by v, like and map operators.
to_bin_string t x use encode_bin to convert x, of type t, to a string.
NOTE: When t is Type.string or Type.bytes, the original buffer x is not prefixed by its size as encode_bin would do. If t is Type.string, the result is x (without copy).
val of_bin_string : 'at->string ->('a, [ `Msg of string ])result
of_bin_string t s is v such that s = to_bin_string t v.
NOTE: When t is Type.string, the result is s (without copy).