andreypopp.com
Using function arguments polymorphically in OCaml
There's a rarely used feature in OCaml called polymorphic record fields. Let's see when and why one might want to use it.
Suppose you have a type of values which can carry a differently typed payload, surfacing the type of the payload as a type parameter (in other terms, a GADT):
type 'a t =
| I : int -> int t
| S : string -> string t
Now let's say we have some such values constructed and we want to process these value by applying a function passed as an argument:
let process : ('a -> unit) -> unit = fun f ->
let i = I 42 in
let s = S "hello" in
f i;
f s
^
This expression has type string t but an expression was expected of type int t
Type string is not compatible with type int
Note how f
was specialized to to be int -> unit
after the first application
so the second application fails with a type error.
This is because only the let
bindings are polymorphic in OCaml, the
arguments passed to functions are not.
This is where the polymorphic record fields are useful. We can define a record type like this:
type f = { f : 'a. 'a t -> unit }
Then we can make an argument not a function but of the record type:
let process { f } =
let i = I 42 in
let s = S "hello" in
f i;
f s
This typechecks nicely and works as expected. The downside is that on the application side it's a bit more verbose:
let () =
let f _ = print_endline "hello" in
process { f }
Note OCaml's manual has another example of using polymorphic record fields.