polymorphismocamlfirst-class-modules

Include module signature and value of this module type in a function signature


I just want to have a simple function that is generic over Hashtbls so I wrote this:

let iter_htbl (type a) (module H : Hashtbl.S with type key = a) htbl =
  H.iter (fun _key _v -> ()) htbl

Which gives me the following error:

53 |   H.iter (fun _key _v -> ()) htbl
                                  ^^^^
Error: This expression has type 'a but an expression was expected of type
         'b H.t
       The type constructor H.t would escape its scope

And if I write:

let iter_htbl (type a b) (module H : Hashtbl.S with type key = a) (htbl : b H.t)
    =
  H.iter (fun _key _v -> ()) htbl

New error (but almost the same):

52 | let iter_htbl (type a b) (module H : Hashtbl.S with type key = a) (htbl : b H.t)
                                                                       ^^^^^^^^^^^^^^
Error: This pattern matches values of type b H.t
       but a pattern was expected which matches values of type 'a
       The type constructor H.t would escape its scope

Is there a solution for what I'm trying to achieve?

Of course, in my simple case I could write

let iter_htbl iter htbl =
  iter (fun _key _v -> ()) htbl

But if I want to use multiple functions from H this is not a useful solution.


Solution

  • That won't work. Unfortunately, OCaml's type system cannot express this directly. That would require higher-kinded polymorphism, which the ML core language does not have:

    let iter_htbl (type key) (type 'a htbl) (type a) (module H : Hashtbl.S with type key = key with type 'a t = 'a htbl) (htbl : a htbl) = ...
    

    The (type 'a htbl) there is a syntax error – for a reason, because that kind of polymorphism over a parameterised type cannot easily be reconciled with type inference (at least not without certain restrictions), as it would require higher-order unification in the general case, which is undecidable.

    The only way to abstract over a parameterised type like H.t in ML is by lifting to the module level and writing a functor:

    module IterHtbl (H : Hashtbl.S) (T : sig type a val htbl : a H.t end) =
    struct
      let it = H.iter (fun _ _ -> ()) T.htbl
    end
    

    That works, because modules are more explicitly typed.