Writing a Function Whose Argument is a Polymorphic Function in OCaml
In OCaml, it is not possible to write a function whose argument is a polymorphic function. Trying to write such a function results in the type-checker complaining back at you.
let foo (type a b) id (x : a) (y : b) = (id x, id y)
Line 1, characters 50-51:
1 | let foo (type a b) id (x : a) (y : b) = (id x, id y);;
^
Error: This expression has type b but an expression was expected
of type a
When OCaml tries to type check foo
, it infers id
expects an
argument of type a
because of id x
, then fails when trying
to type check id y
.
The trick to be able to write foo
is to use records. Indeed, while
the argument of a function cannot be polymorphic, the field of a record can.
This effectively makes it possible to write foo
, at the cost of a
level of indirection.
type id = {id : 'a. 'a -> 'a}
let foo {id} x y = (id x, id y)
From a runtime perspective, it is possible to tell OCaml to remove the
introduced indirection with the unboxed
annotation. There is nothing
we can do in the source, though. We need to destruct id
in
foo
, and we need to construct it at its call site.
g {id = fun x -> x}
As a consequence, this solution is not a silver bullet, but it is an option that is worth considering if, e.g., it allows us to export a cleaner API to the consumer of a module. Personally, I have been considering this trick recently to remove the need for a library to be implemented as a functor.