Reputation: 14295
Function f
argument type is [< 'A | 'B]
that's what I want.
# let rec f = function
| `A -> 0
| `B -> let _ = f in 1
;;
val f : [< `A | `B ] -> int = <fun>
However, if I call it recursively with 'A
it infers an undesired for me type [< 'A | 'B > 'A]
which requires at least 'A
:
# let rec f = function
| `A -> 0
| `B -> let _ = f `A in 1
;;
val f : [< `A | `B > `A ] -> int = <fun>
I still need to recursively call f 'A
, but how do I keep the type [< 'A | 'B]
?
Upvotes: 1
Views: 322
Reputation: 35280
This is yet another instantiation of the let
-polymorphism constraints, that hinders the usage of polymorphic recursive function. Since, OCaml 3.12 we have an explicit way to declare that your function is polymorphic.
Your case is a little bit more complex, since you have implicit type variable, that occurs inside the row-polymorphic type. Maybe there is a better way, but my approach is to make this type variable explicit, with the following type definition
type 'a t = 'a constraint 'a = [< `A | `B]
With such handy type, it is easy to write a proper annotation for a function:
let rec f : 'a . 'a t -> int = function
| `A -> 0
| `B -> let _ = f `A in 1
Just in case, if you don't want to expose this 'a t
, that's ok, since you're not required, 'a t
is equal to [< 'A | 'B]
it just makes 'a
type variable explicit:
module M : sig
val f : [< `A | `B] -> int
end = struct
let rec f : 'a . 'a t -> int = function
| `A -> 0
| `B -> let _ = f `A in 1
end;;
Without introducing 'a t
type, you can make it with a little bit uglier (but this is of course a matter of taste) notation:
let rec f : 'a . ([< `A | `B] as 'a) -> int = function
| `A -> 0
| `B -> let _ = f `A in 1
Of course, this will not scale, for non trivial types.
Upvotes: 2