Skip to content

janestreet/ppx_fields_conv

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ppx_fields_conv

Generation of accessor and iteration functions for ocaml records.

ppx_fields_conv is a ppx rewriter that can be used to define:

  • first-class values representing record fields
  • additional functions to:
    • get and set record fields
    • iterate and fold over all fields of a record
    • create new record values

Basic Usage

One common use of ppx_fields_conv is to derive accessor functions on record types. For example, one can derive getters on the following simple record type:

module M = struct
  type t =
    { dir : [ `Buy | `Sell ]
    ; quantity : int
    ; price : float
    ; mutable cancelled : bool
    }
  [@@deriving fields ~getters]
end

Which produces functions with the following signatures:

module _ : sig
  type t

  val cancelled : t -> bool @@ portable
  val price : t -> float @@ portable
  val quantity : t -> int @@ portable
  val dir : t -> [ `Buy | `Sell ] @@ portable
end =
  M

Implicit unboxed records

To also derive fields on the implicit unboxed version of a record, add the ~unboxed flag. Getters for an implicit unboxed version are mangled with a "_u" suffix to distinguish them from the boxed version (there are no setters because all unboxed record fields are immutable). All other definitions are put in a module named Fields_of_<typename>_u (or Fields_u if <typename> is t).

See Full list of selectors below for an example.

Selecting definitions

The [@@deriving fields] clause accepts selectors specifying which definitions it should provide. Use ~getters and ~setters to explicitly select toplevel accessors, ~fields to select Field.t values, and ~names to select Fields.names. Use ~iterators with a tuple containing names chosen from create, make_creator, and so on, to select elements of Fields. Use ~direct_iterators with a tuple of names to select elements of Fields.Direct. For example:

type t = { x : int; y : int }
[@@deriving fields ~getters ~fields ~iterators:(fold, iter)]

The above defines the accessors x and y, the field values Fields.x and Fields.y, Fields.fold, and Fields.iter.

By default, [@@deriving fields] with no selectors derives all of the functions below except Fields.fold_right and Direct_iterator.fold_right. This behavior can be changed with the -deriving-fields-require-selectors command-line argument, which currently defaults to false. Passing true instead causes [@@derving fields] with no selectors to produce an error during preprocessing. Note that the default behavior derives map_poly, so it doesn't work with records with unboxed fields.

Implicitly-selected definitions

The definitions of several of these functions depend on the getters, setters, and fields definitions. As a result, some of their dependencies might be derived in *.ml files, even if they were not explicitly indicated in the selector list. Specifically, in structures (and in the toplevel of *.ml files):

  • [@@deriving fields ~fields] also derives the functions under ~getters and ~setters
  • [@@deriving fields ~iterators:_] and [@@deriving fields ~direct_iterators:_] (for any legal arguments to iterators and direct_iterators; see below) also derives ~fields (which transitively derives ~getters and ~setters)

It's fine to use these "free" derived functions in the *.ml. However, to expose them in *.mlis, one must explicitly include them in the selector list, since [@@deriving fields], when used in signatures, only derives declarations for exactly the requested functions (and not their dependencies).

Full list of selectors

The full list of permitted selectors and the signatures of the corresponding functions follows:

type t =
  { dir : [ `Buy | `Sell ]
  ; quantity : int
  ; price : float
  ; mutable cancelled : bool
  }
[@@deriving
  fields
    ~unboxed
    ~getters
    ~local_getters
    ~setters
    ~names
    ~fields
    ~iterators:
      ( create
      , make_creator
      , exists
      , fold
      , fold_right
      , for_all
      , iter
      , map
      , to_list
      , map_poly )
    ~direct_iterators:
      (exists, fold, fold_right, for_all, iter, map, to_list, set_all_mutable_fields)]

then code will be generated for functions of the following type (note that the @@ portable annotations are for OxCaml only, and are converted to ignored attributes in regular OCaml):

(* getters *)
val cancelled : t -> bool @@ portable [@@zero_alloc]
val price     : t -> float @@ portable [@@zero_alloc]
val quantity  : t -> int @@ portable [@@zero_alloc]
val dir       : t -> [ `Buy | `Sell ] @@ portable [@@zero_alloc]

(* local getters *)
val cancelled__local : local_ t -> bool @@ portable [@@zero_alloc]
val price__local     : local_ t -> local_ float @@ portable [@@zero_alloc]
val quantity__local  : local_ t -> local_ int @@ portable [@@zero_alloc]
val dir__local       : local_ t -> local_ [ `Buy | `Sell ] @@ portable [@@zero_alloc]

(* setters *)
val set_cancelled : t -> bool -> unit @@ portable [@@zero_alloc]

(* getters for t# *)
val cancelled_u : t# -> bool @@ portable [@@zero_alloc]
val price_u     : t# -> float @@ portable [@@zero_alloc]
val quantity_u  : t# -> int @@ portable [@@zero_alloc]
val dir_u       : t# -> [ `Buy | `Sell ] @@ portable [@@zero_alloc]

(* local getters for t# *)
val cancelled_u__local : local_ t# -> local_ bool @@ portable [@@zero_alloc]
val price_u__local     : local_ t# -> local_ float @@ portable [@@zero_alloc]
val quantity_u__local  : local_ t# -> local_ int @@ portable [@@zero_alloc]
val dir_u__local       : local_ t# -> local_ [ `Buy | `Sell ] @@ portable [@@zero_alloc]

(* higher order fields and functions over all fields *)
module Fields : sig

  val names : string list @@ portable

  val cancelled : (t, bool            ) Field.t @@ portable
  val price     : (t, float           ) Field.t @@ portable
  val quantity  : (t, int             ) Field.t @@ portable
  val dir       : (t, [ `Buy | `Sell ]) Field.t @@ portable

  val create
    :  dir       : [ `Buy | `Sell ]
    -> quantity  : int
    -> price     : float
    -> cancelled : bool
    -> t
    @@ portable

  val make_creator
    :  dir       : ((t, [ `Buy | `Sell ]) Field.t -> 'a -> ('arg -> [ `Buy | `Sell ]) * 'b)
    -> quantity  : ((t, int             ) Field.t -> 'b -> ('arg -> int             ) * 'c)
    -> price     : ((t, float           ) Field.t -> 'c -> ('arg -> float           ) * 'd)
    -> cancelled : ((t, bool            ) Field.t -> 'd -> ('arg -> bool            ) * 'e)
    -> 'a -> ('arg -> t) * 'e
    @@ portable

  val fold
    :  init      : 'a
    -> dir       : local_ ('a -> (t, [ `Buy | `Sell ]) Field.t -> 'b)
    -> quantity  : local_ ('b -> (t, int             ) Field.t -> 'c)
    -> price     : local_ ('c -> (t, float           ) Field.t -> 'd)
    -> cancelled : local_ ('d -> (t, bool            ) Field.t -> 'e)
    -> 'e
    @@ portable

  val fold_right
    :  dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> 'd -> 'e)
    -> quantity  : local_ ((t, int             ) Field.t -> 'c -> 'd)
    -> price     : local_ ((t, float           ) Field.t -> 'b -> 'c)
    -> cancelled : local_ ((t, bool            ) Field.t -> 'a -> 'b)
    -> init      : 'a
    -> 'e
    @@ portable

  val map
    :  dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> [ `Buy | `Sell ])
    -> quantity  : local_ ((t, int             ) Field.t -> int)
    -> price     : local_ ((t, float           ) Field.t -> float)
    -> cancelled : local_ ((t, bool            ) Field.t -> bool)
    -> t
    @@ portable

  val iter
    :  dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> unit)
    -> quantity  : local_ ((t, int             ) Field.t -> unit)
    -> price     : local_ ((t, float           ) Field.t -> unit)
    -> cancelled : local_ ((t, bool            ) Field.t -> unit)
    -> unit
    @@ portable

  val for_all
    :  dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> bool)
    -> quantity  : local_ ((t, int             ) Field.t -> bool)
    -> price     : local_ ((t, float           ) Field.t -> bool)
    -> cancelled : local_ ((t, bool            ) Field.t -> bool)
    -> bool
    @@ portable

  val exists
    :  dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> bool)
    -> quantity  : local_ ((t, int             ) Field.t -> bool)
    -> price     : local_ ((t, float           ) Field.t -> bool)
    -> cancelled : local_ ((t, bool            ) Field.t -> bool)
    -> bool
    @@ portable

  val to_list
    :  dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> 'a)
    -> quantity  : local_ ((t, int             ) Field.t -> 'a)
    -> price     : local_ ((t, float           ) Field.t -> 'a)
    -> cancelled : local_ ((t, bool            ) Field.t -> 'a)
    -> 'a list
  @@ portable

  val map_poly : local_ ([< `Read | `Set_and_create ], t, 'a) Field.user -> 'a list @@ portable

  (** Functions that take a record directly *)
  module Direct : sig

      val fold
        :  t
        -> init      : 'a
        -> dir       : local_ ('a -> (t, [ `Buy | `Sell ]) Field.t -> t -> [ `Buy | `Sell ] -> 'b)
        -> quantity  : local_ ('b -> (t, int             ) Field.t -> t -> int              -> 'c)
        -> price     : local_ ('c -> (t, float           ) Field.t -> t -> float            -> 'd)
        -> cancelled : local_ ('d -> (t, bool            ) Field.t -> t -> bool             -> 'e)
        -> 'e
        @@ portable

      val fold_right
        :  t
        -> dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> t -> [ `Buy | `Sell ] -> 'd -> 'e)
        -> quantity  : local_ ((t, int             ) Field.t -> t -> int              -> 'c -> 'd)
        -> price     : local_ ((t, float           ) Field.t -> t -> float            -> 'b -> 'c)
        -> cancelled : local_ ((t, bool            ) Field.t -> t -> bool             -> 'a -> 'b)
        -> init:'a
        -> 'e
        @@ portable

      val map
        :  t
        -> dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> t -> [ `Buy | `Sell ] -> [ `Buy | `Sell ])
        -> quantity  : local_ ((t, int             ) Field.t -> t -> int              -> int)
        -> price     : local_ ((t, float           ) Field.t -> t -> float            -> float)
        -> cancelled : local_ ((t, bool            ) Field.t -> t -> bool             -> bool)
        -> t
        @@ portable

      val iter
        :  t
        -> dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> t -> [ `Buy | `Sell ] -> unit)
        -> quantity  : local_ ((t, int             ) Field.t -> t -> int              -> unit)
        -> price     : local_ ((t, float           ) Field.t -> t -> float            -> unit)
        -> cancelled : local_ ((t, bool            ) Field.t -> t -> bool             -> unit)
        -> unit
        @@ portable

      val for_all
        :  t
        -> dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> t -> [ `Buy | `Sell ] -> bool)
        -> quantity  : local_ ((t, int             ) Field.t -> t -> int              -> bool)
        -> price     : local_ ((t, float           ) Field.t -> t -> float            -> bool)
        -> cancelled : local_ ((t, bool            ) Field.t -> t -> bool             -> bool)
        -> bool
        @@ portable

      val exists
        :  t
        -> dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> t -> [ `Buy | `Sell ] -> bool)
        -> quantity  : local_ ((t, int             ) Field.t -> t -> int              -> bool)
        -> price     : local_ ((t, float           ) Field.t -> t -> float            -> bool)
        -> cancelled : local_ ((t, bool            ) Field.t -> t -> bool             -> bool)
        -> bool
        @@ portable

      val to_list
        :  t
        -> dir       : local_ ((t, [ `Buy | `Sell ]) Field.t -> t -> [ `Buy | `Sell ] -> 'a)
        -> quantity  : local_ ((t, int             ) Field.t -> t -> int              -> 'a)
        -> price     : local_ ((t, float           ) Field.t -> t -> float            -> 'a)
        -> cancelled : local_ ((t, bool            ) Field.t -> t -> bool             -> 'a)
        -> 'a list
        @@ portable

      val set_all_mutable_fields : local_ t -> cancelled:bool -> unit @@ portable [@@zero_alloc]
    end
end

module Fields_u : sig
  (* similar to the signature of [Fields], but every [t] has replaced with a
     [t#] and [set_all_mutable_fields] doesn't mutate any fields *)

  val names : string list @@ portable

  val cancelled : (t#, bool            ) Field.t @@ portable
  val price     : (t#, float           ) Field.t @@ portable
  val quantity  : (t#, int             ) Field.t @@ portable
  val dir       : (t#, [ `Buy | `Sell ]) Field.t @@ portable

  val create
    :  dir       : [ `Buy | `Sell ]
    -> quantity  : int
    -> price     : float
    -> cancelled : bool
    -> t#
    @@ portable

  val make_creator
    :  dir       : ((t#, [ `Buy | `Sell ]) Field.t -> 'a -> ('arg -> [ `Buy | `Sell ]) * 'b)
    -> quantity  : ((t#, int             ) Field.t -> 'b -> ('arg -> int             ) * 'c)
    -> price     : ((t#, float           ) Field.t -> 'c -> ('arg -> float           ) * 'd)
    -> cancelled : ((t#, bool            ) Field.t -> 'd -> ('arg -> bool            ) * 'e)
    -> 'a -> ('arg -> t#) * 'e
    @@ portable

  val fold
    :  init      : 'a
    -> dir       : local_ ('a -> (t#, [ `Buy | `Sell ]) Field.t -> 'b)
    -> quantity  : local_ ('b -> (t#, int             ) Field.t -> 'c)
    -> price     : local_ ('c -> (t#, float           ) Field.t -> 'd)
    -> cancelled : local_ ('d -> (t#, bool            ) Field.t -> 'e)
    -> 'e
    @@ portable

  val fold_right
    :  dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> 'd -> 'e)
    -> quantity  : local_ ((t#, int             ) Field.t -> 'c -> 'd)
    -> price     : local_ ((t#, float           ) Field.t -> 'b -> 'c)
    -> cancelled : local_ ((t#, bool            ) Field.t -> 'a -> 'b)
    -> init      : 'a
    -> 'e
    @@ portable

  val map
    :  dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> [ `Buy | `Sell ])
    -> quantity  : local_ ((t#, int             ) Field.t -> int)
    -> price     : local_ ((t#, float           ) Field.t -> float)
    -> cancelled : local_ ((t#, bool            ) Field.t -> bool)
    -> t#
    @@ portable

  val iter
    :  dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> unit)
    -> quantity  : local_ ((t#, int             ) Field.t -> unit)
    -> price     : local_ ((t#, float           ) Field.t -> unit)
    -> cancelled : local_ ((t#, bool            ) Field.t -> unit)
    -> unit
    @@ portable

  val for_all
    :  dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> bool)
    -> quantity  : local_ ((t#, int             ) Field.t -> bool)
    -> price     : local_ ((t#, float           ) Field.t -> bool)
    -> cancelled : local_ ((t#, bool            ) Field.t -> bool)
    -> bool
    @@ portable

  val exists
    :  dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> bool)
    -> quantity  : local_ ((t#, int             ) Field.t -> bool)
    -> price     : local_ ((t#, float           ) Field.t -> bool)
    -> cancelled : local_ ((t#, bool            ) Field.t -> bool)
    -> bool
    @@ portable

  val to_list
    :  dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> 'a)
    -> quantity  : local_ ((t#, int             ) Field.t -> 'a)
    -> price     : local_ ((t#, float           ) Field.t -> 'a)
    -> cancelled : local_ ((t#, bool            ) Field.t -> 'a)
    -> 'a list
  @@ portable

  val map_poly : local_ ([< `Read | `Set_and_create ], t#, 'a) Field.user -> 'a list @@ portable

  (** Functions that take a record directly *)
  module Direct : sig

      val fold
        :  t#
        -> init      : 'a
        -> dir       : local_ ('a -> (t#, [ `Buy | `Sell ]) Field.t -> t# -> [ `Buy | `Sell ] -> 'b)
        -> quantity  : local_ ('b -> (t#, int             ) Field.t -> t# -> int              -> 'c)
        -> price     : local_ ('c -> (t#, float           ) Field.t -> t# -> float            -> 'd)
        -> cancelled : local_ ('d -> (t#, bool            ) Field.t -> t# -> bool             -> 'e)
        -> 'e
        @@ portable

      val fold_right
        :  t#
        -> dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> t# -> [ `Buy | `Sell ] -> 'd -> 'e)
        -> quantity  : local_ ((t#, int             ) Field.t -> t# -> int              -> 'c -> 'd)
        -> price     : local_ ((t#, float           ) Field.t -> t# -> float            -> 'b -> 'c)
        -> cancelled : local_ ((t#, bool            ) Field.t -> t# -> bool             -> 'a -> 'b)
        -> init:'a
        -> 'e
        @@ portable

      val map
        :  t#
        -> dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> t# -> [ `Buy | `Sell ] -> [ `Buy | `Sell ])
        -> quantity  : local_ ((t#, int             ) Field.t -> t# -> int              -> int)
        -> price     : local_ ((t#, float           ) Field.t -> t# -> float            -> float)
        -> cancelled : local_ ((t#, bool            ) Field.t -> t# -> bool             -> bool)
        -> t#
        @@ portable

      val iter
        :  t#
        -> dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> t# -> [ `Buy | `Sell ] -> unit)
        -> quantity  : local_ ((t#, int             ) Field.t -> t# -> int              -> unit)
        -> price     : local_ ((t#, float           ) Field.t -> t# -> float            -> unit)
        -> cancelled : local_ ((t#, bool            ) Field.t -> t# -> bool             -> unit)
        -> unit
        @@ portable

      val for_all
        :  t#
        -> dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> t# -> [ `Buy | `Sell ] -> bool)
        -> quantity  : local_ ((t#, int             ) Field.t -> t# -> int              -> bool)
        -> price     : local_ ((t#, float           ) Field.t -> t# -> float            -> bool)
        -> cancelled : local_ ((t#, bool            ) Field.t -> t# -> bool             -> bool)
        -> bool
        @@ portable

      val exists
        :  t#
        -> dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> t# -> [ `Buy | `Sell ] -> bool)
        -> quantity  : local_ ((t#, int             ) Field.t -> t# -> int              -> bool)
        -> price     : local_ ((t#, float           ) Field.t -> t# -> float            -> bool)
        -> cancelled : local_ ((t#, bool            ) Field.t -> t# -> bool             -> bool)
        -> bool
        @@ portable

      val to_list
        :  t#
        -> dir       : local_ ((t#, [ `Buy | `Sell ]) Field.t -> t# -> [ `Buy | `Sell ] -> 'a)
        -> quantity  : local_ ((t#, int             ) Field.t -> t# -> int              -> 'a)
        -> price     : local_ ((t#, float           ) Field.t -> t# -> float            -> 'a)
        -> cancelled : local_ ((t#, bool            ) Field.t -> t# -> bool             -> 'a)
        -> 'a list
        @@ portable

      val set_all_mutable_fields : local_ t# -> unit @@ portable [@@zero_alloc]
    end
end

Use of [@@deriving fields] in an *.mli will extend the signature for functions with the above types; In an *.ml, definitions will be generated.

Field.t is defined in Fieldslib, including:

type ('perm, 'record, 'field) t_with_perm

type ('record, 'field) t = ([ `Read | `Set_and_create], 'record, 'field) t_with_perm

val name :  (_, _, _) t_with_perm -> string
val get  : (_, 'r, 'a) t_with_perm -> 'r -> 'a

zero_alloc attribute

By default, ppx_fields_conv will generate [@@zero_alloc] attributes for all getters, setters, and set_all_mutable_fields. It is possible, however, that the generated implementations for a given record may actually allocate, causing the compiler to complain (this can happen e.g. if your record contains all float fields, or you mask the generated code with custom definitions).

module Some_float_type = struct
  type t = float
end
type t =
  { x : Some_float_type.t
  ; y : Some_float_type.t
  }
[@@deriving fields ~getters]
Line 2, characters 7-8:
Error: Annotation check for zero_alloc failed on function TOP9.x (camlTOP9__x_172_177_code).
Hint: add [@@fields.no_zero_alloc] to disable the zero-alloc guarantees that [@@deriving fields] tries to make by default.

Line 3, characters 7-8:
Error: Annotation check for zero_alloc failed on function TOP9.y (camlTOP9__y_171_176_code).
Hint: add [@@fields.no_zero_alloc] to disable the zero-alloc guarantees that [@@deriving fields] tries to make by default.

In these instance you can tag your record with an additional attribute [@@fields.no_zero_alloc] to disable adding the [@@zero_alloc] attribute:

type t =
  { x : Some_float_type.t
  ; y : Some_float_type.t
  }
[@@fields.no_zero_alloc]
[@@deriving fields ~getters]

Functions over all fields

Use of the generated functions together with Fieldslib allow us to define functions over t which check exhaustiveness w.r.t record fields, avoiding common semantic errors which can occur when a record is extended with new fields but we forget to update functions.

For example if you are writing a custom equality operator to ignore small price differences:

let ( = ) a b : bool =
  let use op field = op (Field.get field a) (Field.get field b) in
  let price_equal p1 p2 = Float.(abs (p1 - p2) < 0.001) in
  Fields.for_all
    ~dir:(use Poly.equal)
    ~quantity:(use ( = ))
    ~price:(use price_equal)
    ~cancelled:(use Bool.equal)
;;

A type error would occur if you were to add a new field and not change the definition of ( = ):

type t = {
  dir : [ `Buy | `Sell ];
  quantity : int;
  price : float;
  mutable cancelled : bool;
  symbol : string;
} [@@deriving fields ~iterators:(for_all, fold)]
let ( = ) a b : bool =
  let use op field = op (Field.get field a) (Field.get field b) in
  let price_equal p1 p2 = Float.(abs (p1 - p2) < 0.001) in
  Fields.for_all
    ~dir:(use Poly.equal)
    ~quantity:(use ( = ))
    ~price:(use price_equal)
    ~cancelled:(use Bool.equal)
;;
Lines 4-8, characters 5-34:
Error: This expression has type
         symbol:local_
         (([< `Read | `Set_and_create ], t, string) Field.t_with_perm -> bool) ->
         bool
       but an expression was expected of type bool
  Hint: This function application is partial,
  maybe some arguments are missing.

Or similarly you could use fold to create to_string function:

let to_string t =
  let conv to_s acc f =
    Printf.sprintf "%s: %s" (Field.name f) (to_s (Field.get f t)) :: acc
  in
  let fs =
    Fields.fold
      ~init:[]
      ~dir:
        (conv (function
          | `Buy -> "Buy"
          | `Sell -> "Sell"))
      ~quantity:(conv Int.to_string)
      ~price:(conv Float.to_string)
      ~cancelled:(conv Bool.to_string)
  in
  String.concat fs ~sep:", "
;;

Addition of a new field would cause a type error reminding you to update the definition of to_string.

Interaction with ppx_template

Sometimes a record type is templated over the kind of its parameter. For example:

type%template ('a : k) nonemptylist_with_length =
  { hd : 'a
  ; len : int
  ; rest : ('a nonemptylist_with_length [@kind k]) option
  }
[@@deriving fields ~getters ~local_getters ~iterators:create ~direct_iterators:to_list]
[@@kind k = (value, bits64, float64)]

Getters and other functions that are derived at the top-level are generated with each name templated the same way as the corresponding type. For example, the hd getter for list_with_length [@kind bits64] is hd [@kind bits64]. Here is an example client of these getters:

let%template[@kind k = (value, bits64, float64)] [@mode m = (global, local)] validate_lengths
  (l @ m)
  =
  let rec inner_loop l =
    let len_expected =
      match (rest [@kind k] [@mode m]) l with
      | Some rest -> inner_loop rest
      | None -> 1
    in
    assert ((len [@kind k] [@mode m]) l = len_expected);
    len_expected + 1
  in
  ignore (inner_loop l : int)
;;

Functions derived in Fields are not themselves templated: rather, the whole module Fields is templated:

let%template[@kind k = (value, bits64, float64)] __ =
  let module Fields = Fields_of_nonemptylist_with_length [@kind k] in
  let _ = Fields.create in
  let _ = Fields.Direct.to_list in
  ()
;;

Note that map_poly cannot currently be used with non-value record fields. Reach out to the maintainers if you have an example of a case where this would be useful.

type t = { x : int64# } [@@deriving fields ~iterators:map_poly]
Line 1, characters 6-7:
Error: This expression has type
         ([< `Read | `Set_and_create ], t, int64#) Field.t_with_perm
       but an expression was expected of type
         ([< `Read | `Set_and_create ], t, 'a) Field.t_with_perm
       Hint: did you derive [fields ~iterators:map_poly] on a record with non-value fields?
       The layout of int64# is bits64
         because it is the unboxed version of the primitive type int64.
       But the layout of int64# must be a sublayout of value
         because of the annotation on the wildcard _ at line 1, characters 6-7.

About

Generation of accessor and iteration functions for ocaml records

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 5

Languages