Partial application and '_weak1 type variable that cannot be generalized

Hi !
I wonder why

let skip_none =
  let rec skip_none acc_l = function
    | None :: tl -> skip_none acc_l tl
    | Some e :: tl -> skip_none (e::acc_l) tl
    | [] -> acc_l
  in
  skip_none []

gives following compiler error:

34 | let skip_none =
         ^^^^^^^^^
Error: The type of this expression, '_weak1 option list -> '_weak1 list,
       contains type variables that cannot be generalized

while this:

let skip_none l =
  let rec skip_none acc_l = function
    | None :: tl -> skip_none acc_l tl
    | Some e :: tl -> skip_none (e::acc_l) tl
    | [] -> acc_l
  in
  skip_none [] l

compiles fine. (only variable “l” and the first and last line changes) It is not really a problem, I am just curious of how it works :slight_smile: Do you have an idea ?

This is the value restriction at play.
In short the result of a function application may refer to some captured mutable state, and mutable states should not be polymorphic. For instance, if I define a function swap like this

let store = ref None
let swap x = match !store with
| Some y -> store := Some x; y
| None -> store := Some x; x

subsequent arguments should have the same type.
To avoid this issue, the easiest solution is eta-expansion (transforming expr into fun x -> expr x). Indeed, if skip_none [] is syntactically the result of a computation and thus cannot be polymorphic, this is not the case of fun l -> skip_none [] l since the function will only be evaluated once the argument l is provided.

1 Like

It’s worth noting that function definitions like your original one are perfectly legal. What isn’t legal is exposing weak type variables in module types. I suspect the compiler is yelling at you because you have this code in a .ml file with no corresponding .mli file; the compiler tries to infer a module type, and cannot do so.

1 Like

Thanks a lot @octachron , your example is crystal clear, I feel I understand now.
@Levi_Roth, indeed what you say is correct, I exposed the function without mli file. Thanks for sharing that I could use this with a proper mli file.
Have both a good day !