Deriving Core_kernel.Hashtbl

I’d like to put a hashtable inside a struct which is comparable (via [@@deriving eq]), in v2 of Real World OCaml I found that I can use the .M functor to create a new module which I can derive:

type string_int_map = int Map.M(String).t

which is more or less equivalent to the more common

type string_int_map = (string, int) Map.t

However, if I try the same with a Hashtbl:

type string_int_table = int Hashtbl.M(String).t [@@deriving eq]

I get the following error:

File "_none_", line 1:
Error: broken invariant in parsetree: Functor application not allowed here.

Removing the [@@deriving eq] fixes is, and I get the expected error:

File "_none_", line 1:
Error: Unbound value equal_string_int_table

So, my question is, how can I convince ppx_deriving to correctly derive the module?

This looks like a bug in ppx_deriving.eq. It generates the following piece of code:

let rec equal : t -> t -> Ppx_deriving_runtime.bool =
  let __0 () = Hashtbl.Make(M).equal in
  ...

and this is not valid OCaml. More precisely, it’s building an AST that does not correspond to an actual program. ppx_deriving could detect that and give a better error message. As for your program, it might help to give a name to the module:

module StringHashtbl = Hashtbl.M(String)
type string_int_table = int StringHashtbl.t [@@deriving eq]

That should work if there’s an equal function in the output of this functor.

First of all, it should be equal not eq. Second, it all depends on a version of the JS suite that you’re using, as well as whether you’re using Base, or Core_kernel, or Core. For example, for the v0.11 version, the following works

type t = int Base.Map.M(Int).t [@@deriving compare]

The [@@deriving equal] variant will not work, as it was added rather recently. Not sure when, they do not not update their changelogs.

Note, that the following

open Core_kernel
type t = int Map.M(Int).t [@@deriving compare]

will work only after v0.12. Concerning the hash tables, for v0.11 the following doesn’t work:

type t = int Base.Hashtbl.M(Int).t [@@deriving compare]
Error: Unbound value Base.Hashtbl.compare_m__t

Not sure, whether it is already fixed in v0.12.

To summarize, currently the functorial interfaces, albeit being a really nice idea, do no work with ppx_derivers, but the support is slowly coming.

ppx_compare support for maps is available since v0.12, with ppx_bin_prot coming soon.

ppx_compare for hash tables, and in fact Hashtbl.compare/Int.Table.compare do not exist, and probably won’t be added. If you’re using a hash table as a key for some data structure, you’re probably making a mistake :slight_smile:

1 Like

You’re thinking about ppx_compare. This was using ppx_deriving, which is clearly showing eq, not equal: https://github.com/ocaml-ppx/ppx_deriving#plugins-eq-and-ord.

Ahh, that’s it. This does in fact work (with the only problem, that the functor does not include an equal function).
My solution was to implement the derived function myself, which was reasonably simple:

let equal_string_int_table a b =
  Hashtbl.equal a b Int.(=)

Yep, that’s what I was saying, maybe in a slightly contracted form. You shall use equal because you need to use ppx_compare from the ppx_jane suite, not ppx_deriving, which will be triggered by using the eq deriver.

The support of X.M(T).t doesn’t come automatically, it is a special convention in the Janestreet suite of libraries. So, for it to work, you need to use the derivers that are aware of such convention.

1 Like

This is a fair criticism, but we are working on it. We’re now incrementally creating public-facing changelogs as we go, so the quality of changelogs for the next stable release should be a lot better.

We did publish a changelog on discuss, which does include mention of [@@deriving equal]:

2 Likes

Alright, now I’ve understood it.
Thank you!