Source

meta_conv /

Filename Size Date modified Message
json
lib
ocaml
opam
pa
56 B
1.1 KB
243 B
20.5 KB
10.1 KB
456 B
145.2 KB

Meta_conv

Using Meta_conv

This is for whom uses already prepared conv(...) encoders and decoders.

Module open

The following modules should be open in the module where you want to use conv(xyz) things:

  • Meta_conv.Open: for types with special conversion like mc_option, mc_leftovers.
  • Module for primitive decoders and encoders, such as xyz_of_int, int_of_xyz, etc.. By convention, it is Xyz_conv, but you can use your own primitive decoders.

Other Meta_conv modules like Meta_conv.Internal are only for decoder/encoder implementers. Normally you do not need it. Unless you implement a new conv(...), you do not need to open or use this module.

with conv(...) extension

For example:

open Meta_conv.Open
open Json_conv

type t = {
     foo: int;
     bar: float;
} with conv(json)

type t = Foo of int
       | Bar of float
with conv(json)

conv(xyz)

conv(xyz) generates codes for encoders and decoders between the target type Xyz.t and host types whose definitions attached with conv(xyz). It requires the following types and values are defined and accessible in the context:

  • Xyz.t, the target type
  • Xyz_conv module which defines generic decoders and encoders, and decoding error exception. See Meta_conv.Internal.MinimumCoders for details.
  • <xyz>_of_<prims> and <prims>_of_<xyz>, for primitive types <prim> such as int, bool, float, etc.. Usually they are defined in Xyz_conv module.

conv(xyz) is actually a short cut of conv(xyz, Xyz.t, Xyz_conv). This three ary form of conv(...) can control of names of the target type and decoder/encoder module.

Limitations

Complex types such as open polymorphic variants, open object types and GADTs are not supported.

What to be generated

If a type definition of t (or type definitions concatenated with and) is attached with with conv(xyz) notation, CamlP4 module pa_meta_conv generates the following function definitions:

  • xyz_of_t : Encoder of the host type t to the target type xyz
  • t_of_xyz : Decoder of the target type xyz to the host type t, result monad version
  • t_of_xyz_exn : Decoder of the target type xyz to the host type t, exception version

The differece of t_of_xyz and t_of_xyz_exn is the error handling. No exn version reports the decoding error as a value `Error (...), while the exn version raises an exception Xyz_conv.Error.

Meta_conv annotations

Meta_conv annotations (annots) are to control data conversion between OCaml data types and encoded data. Annots can be written at several places in type definitions.

Annots at Data type name

Annots at Data type names can be listed by commas: (: Ignore_unknown_fields, field_check = ... :)

(: Ignore_unknown_fields :) for records and named object types

Ignore excess record fields at decoding:

type t (: Ignore_unknown_fields :) = {
     foo: int;
     bar: float;
  } with conv(json)

Without Ignore_unknown_fields, t_of_json fails if it takes a json with extra fields other than foo or bar. With Ignore_uknown_fields, the extra fields are just discarded.

Ignore_unknown_fields can be overridden by following field_check = <e>.

(: field_check = <e> :) for records and named object types

Use a custom field check <e> at decoding:

type t (: field_check = my_custom_field_checker :) = {
     foo: int;
     bar: float;
  } with conv(json)

The expression <e> must have the same type as Meta_conv.Internal.record_unknown_field_check. <e> is syntactically embeded in the decoder code and executed at each call of decoder function.

field_check = <e> can be overridden by following field_check = <e> or Ignore_unknown_fields.

(: one_of :) for variants and named poly variant types

Decoder of one_of annot tries to decode the target data using the decoders of constructor argument types. It sequentially tries the decoders of the constructors from the top to the bottom, and the first successful result is used:

type t (: one_of :) =
  | Int of int
  | Float of float
  | String of string
with conv(json)

For example, json_of_t of the above first tries to decode the target input as int. If successful, it returns Int <result>. If the decoding as an integer fails, it tries to decode the input as float. If it fails, it tries the last one, the decoder for string.

Note that one_of just tries decoders sequentially. Its efficiency heavily depends on those of sub decoders and their declaration order.

Annots at Variant tag names and Record field names

as "name"

Use as "name" for tag encoding instead of the constructor name itself. Usually it is used to have lowercased variant tag names and uppercase record field names:

type t = Hg as "hg" | Git as "git" with conv(json)

type t = Name as "name" of string with conv(json)

type t = { name as "Name" : string } with conv(json)

type t = { type_ as "type" : string } with conv(json)

Special type names

Type names whose decoding/encoding work specially

(<host_type>, <target_type>) mc_result

This is equal to (<host_type>, <target_type> Error.t) Result.t.

The decoding and encoding work specially if the target type specified by conv(...) is equal to <targe_type>. Otherwise, its codings are done normally.

If the target type matches with conv(...), the target value is decoded using the decoder of <host_type>. However, the result is wrapped by the result type: any decoding error is reported as a successful result embeded in the result type (<host_type>, <target_type> Error.t) Result.t. Encoding of this result type is done in the opposite way: the encoding of `Error(desc, target, trace) is target.

Type names work specially only when used as record field types

These type names work only specially when used as record field types: like { label : t mc_option }, but not like { label : t mc_option list }.

t mc_option

t sexp_option

If an OCaml record field has type t mc_option, the field is considererd optional in the target record. If the target record misses these fields, None is used for them in the host value. If the host value has None for these fields, they are omitted in the target record.

sexp_option works as same as mc_option, introduced to live with sexplib nicely.

<target_type> mc_leftovers

<target_type> mc_leftovers is equal to (string, <target_type>) list.

The decoding and encoding work specially if the target type specified by conv(...) is equal to <targe_type>. Otherwise, its codings are done normally.

If the target type matches with conv(...), at decoding, the field with mc_leftovers takes the target record fields which are not listed in the other OCaml record fields, in undecoded format. Encoding of mc_leftovers field uses its raw record value ''as is''. The field name of the OCaml record does not appear as a tag in the encoded data.

  • The type of the field must be <target_type> mc_leftovers
  • mc_leftovers record field can exist at most one for an OCaml record type. (CHECK IS NOT YET IMPLEMENTED)
  • You cannot specify with Ignore_unknown_fields type name annotation.

t mc_embeded

Writing encoders and decoders for a new target type

Modules and values to be accessible

For actual example, see an example in meta_conv/json or meta_conv/ocaml directory.

Meta_conv

Meta_conv packed module library must be linked with programs which use meta_conv.

Conversion module

The conversion module for the basic encoders and decoders must be accessible in the name given at with conv(...) in the context. For example, if you write with conv(json, Json.t, Json_conv), Json_conv of the following signature must exist:

type target = Json.t

module Encode : sig
  val tuple        : target list -> target
  val variant      : string -> target list -> target
  val poly_variant : string -> target list -> target
  val record       : (string * target) list -> target
  val object_      : (string * target) list -> target
end

module Decode : sig
  val tuple        : target -> target list
  val variant      : target -> string * target list
  val poly_variant : target -> string * target list
  val record       : target -> (string * target) list
  val object_      : target -> (string * target) list
end

type 'a encoder = ('a, target) Encoder.t

exception Error of target Error.t
type 'a decoder = ('a, target) Decoder.t
type 'a decoder_exn = ('a, target) Decoder.t_exn

val from_Ok : [< `Error of target Error.t | `Ok of 'a ] -> 'a
(** If the argument is [`Error e], raises [Error e] *)

it is equivalent with:

Meta_conv.Internal.MinimumCoders with type target = Json.t

Encoder and decoder functions for primitive types

The encoder and decoder functions for primitive types (int, int32, int64, nativeint, char, string, float, list, array, bool, lazy_t, option) must be accessible in the context. For example, for conv(json), those have names like int_of_json and json_of_int.