package depyt
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=61dab6ed22949133f3f7763e66d357cf8bdc97fa4b4efc2642fd3bd8d7c0d478
sha512=4c420471d658f9fbe7157e65784e7528a496561facdab618a8d8c89e98e1e91999348fc65592e6b3fe8bd11ecb93f5c719fa1a747b6ab71dbda13176585eb629
doc/depyt/Depyt/index.html
Module Depyt
Source
Yet-an-other type combinator library
Depyt
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.
Depyt
is a modern reboot of Dyntype but using GADTs-based combinators instead of syntax-extensions. When we originally wrote Dyntype
(in 2012) GADTs were not available in OCaml and camlp4 was everywhere -- this is not the case anymore. Finally, Depyt
avoids some of the performance caveats present in Dyntype
by avoiding allocating and converting between intermediate formats.
0.3.0 — homepage
Depyt
The type for runtime representation of values of type 'a
.
Primitives
triple x y z
is a representation of values of type x * y * z
.
Records
The type for fields holding values of type 'b
and belonging to a record 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)
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
.
sealr r
seal the open record r
.
r |+ f
adds the field f
to the open record r
.
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
The type for representing variant cases of type 'a
with patterns of type 'b
.
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
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)
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
.
sealv v
seals the open variant v
.
v |~ c
is v
augmented with the case c
.
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
Depyt
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).
mu f
is the representation r
such that r = mu r
.
For instance:
type x = { x: x option }
let x = mu (fun x ->
record "x" (fun x -> { x })
|+ field "x" x (fun x -> x.x)
|> sealr)
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.
like x f g
is the description of a type which looks like x
using the bijetion (f, g)
.
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.
equal t
is the equality function between values of type t
.
Binary serialization
write t
serializes values of type t
. Use size_of
to pre-determine the size of the buffer.
read t
reads a serialization of a value of type t
.
JSON converters
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.str "%a\n" (pp t) { foo = None; bar = ["foo"] }
(* s is "{ foo = None; bar = [\"foo\"]; }" *)
let j = Fmt.str "%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 ifNone
, 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.
decode_json t e
decodes values of type t
from the jsonm decoder e
.
decode_json_lexemes
is similar to decode_json
but use an already decoded list of JSON lexemes instead of a decoder.