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 to define the runtime representation of recursive types.
Type Combinators
type'a t
The type for runtime representation of values of type 'a.
field n t g is the representation of the field n of type t with getter g.
For instance:
type t = { foo: string option }
let foo = field "foo" (option string) (fun t -> t.x)
type('a, 'b, 'c) open_record
The type for representing open records of type 'a with constructors of type 'b. 'c represents the fields missings to the record, e.g. an open record initially holds 'c = 'b and it can can be sealed when 'c = 'a.
record n f fs is the representation of the record called n of type 'a using f as constructor and with the fields fs.
Putting all together:
type t = { foo: string; bar = (int * string) list; }
let t =
record "t" (fun foo -> { foo })
|+ field "foo" string (fun t -> t.foo)
|+ field "bar" (list (pair int string)) (fun t -> t.bar)
|> sealr
Variants
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.
case0 n v is a representation of a variant case n with no argument and a singleton pattern. e.g.
type t = Foo
let foo = case0 "Foo" Foo
val case1 : string ->'bt->('b->'a)->('a, 'b->'acase_p)case
case1 n t c is a representation of a variant case n with 1 argument of type t and a pattern c an function with one argument of type t. e.g.
type t = Foo of string
let foo = case1 "Foo" string (fun s -> Foo s)
type('a, 'b, 'c) open_variant
The type for representing open variants of type 'a with pattern matching of type 'b. 'c represents the missing cases for the variant, e.g. initially variant hols c' = 'b and it can be sealed when 'c = 'a.
val variant : string ->'b->('a, 'b, 'b)open_variant
variant n c p is a representation of a variant type containing the cases c and using p to deconstruct values.
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)
|> sealr
enum n l is a representation of the variant type which has only constant variant case. e.g.
type t = Foo | Bar | Toto
let t = enum "t" ["Foo", Foo; "Bar", Bar; "Toto", Toto]
Recursive definitions
Type allows to create a limited form 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)
Bijections
Sometimes it is not always possible to describe precisely a type (or it could be too tedious) and it is easier to describe the relation with an other know type. This is what bijections are about.
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:
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 record fields of type 'a option are automatically unboxed in their JSON representation. If the value if None, the field is removed from the JSON object.
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. That's the responsibility of the caller to ensure that the encoded JSON fragment fits properly into a well-formed JSON object.