ocamlfoldocamlfind

Type of generated fold, iter and map not coherent with ppx_deriving documentation


I am struggling to properly set up my OCaml environment to use ppx derivers map, fold and iter, as devined here: https://github.com/ocaml-ppx/ppx_deriving#plugins-iter-map-and-fold

My minimal example is here (I am using Base since this is a library I am using in my wider project):

open Base;;

type data = Row of float array | Dim of data array
[@@deriving iter, map, fold, show];;

let t = Row [|2.;2.|];;
pp_data Caml.Format.std_formatter t;;
map_data (fun x -> x +. 1.) t;;
pp_data Caml.Format.std_formatter t;;

The following code is compiled with ocamlfind ocamlc -package base -package ppx_deriving.iter -package ppx_deriving.map -package ppx_deriving.fold -package ppx_deriving.show -linkpkg -g test.ml && ./a.out; I get a compilation error stating that map_data has type data -> data. But according to the documentation and my general knowledge, map gets a function and a mappable structure, which seems not to be the case here. Testing this in utop gives me the same error.

Is there anything I am missing?

Thank you in advance :)


Solution

  • These derivers work on polymorphic data structures and apply a user function to all values corresponding to the type variables of that structure. Since you don't have any type variables the generated map_data function is deficient, but quite natural, as an absence of the type variable implies a constant function.

    In other words, the general structure of the map_x function for some polymorphic type ('s1, ..., 'sN) x with N type variables is

    ('s1 -> 't1) -> ... -> ('sN -> 'tN) -> ('s1,...,'sN) x -> ('t1,...,'tN) x
    

    I.e., for each type variable it expects a function that maps values of that type to some other type so that the number of arguments to the map function is N+1 In your case since you have zero type variables, there are no mapping functions so you have just x -> x.

    If you will redefine your type as

    type 'a data = Row of 'a array | Dim of 'a data array
    [@@deriving iter, map, fold, show]
    

    You will get map_data with the expect type ('a -> 'b) -> 'a data -> 'b data. The deriver will even understand that an array is a data structure and recurse into it, e.g.,

    let input = Dim [|Row [|1;2;3|]; Row [|3;4;5|]|]
    map_data (fun x -> x + 1) input;;
    - : int data = Dim [|Row [|2; 3; 4|]; Row [|4; 5; 6|]|]
    

    Of course, if you don't want a polymorphic type in your interface, you can always create a type alias, e.g.,

    type t = float data
    

    and expose map_data as

    val map_data : (float -> float) -> t -> t