Legend:
Library
Module
Module type
Parameter
Class
Class type
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.
Type Combinators
type'a t
The type for runtime representation of values of type 'a.
type len = [
| `Int
| `Int8
| `Int16
| `Int32
| `Int64
| `Fixed of int
]
The type of integer used to store buffers, list or array lengths.
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.
type('a, 'b) field
The type for fields holding values of type 'b and belonging to a record of type 'a.
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
Variants
type('a, 'b, 'c) open_variant
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.
type('a, 'b) case
The type for representing variant cases of type 'a with patterns of type 'b.
type'a case_p
The type for representing patterns for a variant of type 'a.
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.
val decode_json :
'at->Jsonm.decoder ->('a, [ `Msg of string ])Stdlib.result
decode_json t e decodes values of type t from the jsonm decoder e.
val decode_json_lexemes :
'at->Jsonm.lexeme list->('a, [ `Msg of string ])Stdlib.result
decode_json_lexemes is similar to decode_json but uses an already decoded list of JSON lexemes instead of a decoder.
val to_json_string : ?minify:bool ->'at->'a-> string
to_json_string is encode_json with a string encoder.
val of_json_string : 'at->string ->('a, [ `Msg of string ])Stdlib.result
of_json_string is decode_json with a string decoder .
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 ])Stdlib.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).