Using Meta_conv

Module open

To use special type conversion correctly, any modules which use meta_conv must open Meta_conv.Open module.

Decoders use types from Meta_conv.Types. You may want to open or create an alias to this module.

Meta_conv.Internal is only for decoder/encoder implementation. 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

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

type t = Foo of int
       | Bar of float
with conv(json, Json.t, Json_conv)

conv takes comma separated arguments to specify the encoder and decoder.

conv(<name>, <target_type>, <converter_module>)


The name of the converter. Meta_conv creates the following funcitons:


The encoder from the OCaml host type to the target type.



The decoders from the target type to the OCaml host type.

<host_type>_of_<name> is monadic style, whose return type is [`Ok of host_type | `Error of ...] .

<host_type>_of_<name>_exn is non monadic style, whose return type is simple host_type and raises an exception at decoding error.


The target type.


The module of conversion functions between the host and target types. The converter module must provide basic encoders and decoders in the signature of Meta_conv.Internal.S. It should also provide encoders and decoders for the basic OCaml primitive types like <name>_of_int. See Json_conv module for an example.

conv(<name>, <target_type>)

The converter module name is deduced from the <target_type>.

If <target_type> is then the convert module is A.B.C_conv.

If <target_type> has no module part, the convert module is Conv.


The target type name is deduced from the <name>. If <name> is json then the target type name is Json.t. The converter module is deduced from the the target type name.

For example, conv(json) is equivalent with conv(json, Json.t, Json_conv).


Polymorphic variants, classes and GADTs are not supported.

Modules and values to be accessible

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


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 t = Json.t

  module Encode : sig
    val tuple : t list -> t
    val variant : string -> t list -> t
    val record : (string * t) list -> t

  module Decode : sig
    (* Address.t = Meta_conv.Internal.Address.t *)
    val tuple   : (t * Address.t) -> (t * Address.t) list
    val variant : (t * Address.t) -> string * (t * Address.t) list
    val record  : (t * Address.t) -> (string * (t * Address.t)) list

An exception for decoding error:

exception Error of <target_type> error

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.

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 :)

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> :)

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.

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 at record fields

t mc_option t sexp_option

If an OCaml record field has type t optional, it works as same as t option (: Optional :): the field is considererd optional in the target record.

sexp_optional is the same as mc_optional, introduced to live with sexplib nicely.

Annots at Record fields

<target_type> mc_leftovers

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.

  • The type of the field must be <target_type> mc_leftovers
  • To fix the target type to one target type, you cannot specify more than one conv(...).
  • 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.
  • 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. You cannot specify "name" target tag.