This is actually an very interesting question.
I didn't know the answer before, but by reading through the two answers before me, plus a bit research, I will try explain and answer below.
Basically, what you want to achieve is the signature like this
val test : bool option -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c
First of all, I have to emphasise that trying to force the parameters to be different polymorphic types, i.e., use 'a, 'b, 'c
etc, will not necessarily force OCaml compiler to think that the parameters must have different types.
for example, if you do
let f (x:'a) (y:'b) = x + y;;
It seems you are forcing x and y to be different types, but after compiling, it gives
val f : int -> int -> int = < fun >
Basically, OCaml will anyway do its type inference
, and apply the conclusion if it is not just against the forced type.
You may think the 'a
and 'b
in let f (x:'a) (y:'b) = x + y;;
to be maybe x and y will have different types and also possibly same types. So it is pointless to force types of parameters like that, right?
So, let's remove all the types forced on parameters and we get
let rec test b f x y =
match b with
| None -> (fst f) x y
| Some true -> test None f x y
| Some false -> test None (swap f) y x
The type of test
will be given by OCaml like this:
val test : bool option -> ('a -> 'a -> 'b) * ('a -> 'a -> 'b) -> 'a ->
'a -> 'b = < fun >
So, basically, OCaml thinks x
and y
must have the same types, and c
is not there because the next available type tag for OCaml to use is 'b
.
Why x and y must have same types?
When OCaml meets let rec test b f x y
, ok, it will think x
has type of 'a
and y
has type of 'b'
.
When OCaml meets | Some true -> test None f x y
, no problem, the above type inference still stand because you are pass same x
and y
to test
again.
The the funny part is when OCaml meets | Some false -> test None (swap f) y x
. You are trying pass y
and x
(notice the order) to test
. In order to let test
work, x
and y
must have the same type, right?
Basically, above is what the counter side of polymorphism recursion @Jeffrey Scofield answered.
polymorphism recursion
means, a function can have some parameters whose types can be changed during recursion, instead of stay constant.
OCaml by default of course only allow constant parameter types.
So how does rank 2 polymorphism
work?
So how to solve it?
You need a for_all
type 'a.
. have a look at my question: In OCaml, what type definition is this: 'a. unit -> 'a.
If we use 'a.
or 'a 'b.
in type definition, then it means it is really for all types, real polymorphic types, and please, ocaml, do not narrow them down as long as it does not harm
.
let rec test : 'a 'b. bool option -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c =
fun b f x y ->
match b with
| None -> (fst f) x y
| Some true -> test None f x y
| Some false -> test None (swap f) y x
Above is the new version of test.
You force type with 'a 'b.
for the function test
and for 'a
and 'b
, ocaml will think they are really both polymorphic, and thus the parameter x
and y
can be accepted in both orders.