It is not possible to generalize make (fun x -> x)
right now in OCaml, because the type checker is not tracking purity of expressions, i.e., it doesn’t know that make f
is C f
and assumes that make
is a arbitrary function of type ('a -> 'b) -> ('a,'b) t
, which, in particular, could be a partial application, that hides a reference to the ('a -> 'b)
parameter inside, so that ('a -> 'b)
type could escape the scope of the weak_id
definition, therefore it must not be generalized.
We can see, however, that let id = C (fun x -> x)
is generalized. This is because OCaml knows that constructor applications are pure expressions. The same is true for let id = {f = fun x -> x}
provided that we have ('a,'b) t = { f : 'a -> 'b }
type.
Therefore, right now the only way to prove to the type checker that your expression is pure, is to use a constructor. You can use the private
keyword if you want more abstractness and control on the module invariants, e.g.,
module M : sig
type ('a,'b) t = private {f : 'a -> 'b }
end = struct
type ('a,'b) = {f : 'a -> 'b}
end
and expect the users of your interface to say M.{f=x}
instead of M.make x
to retain the purity.
With that said, the work on tracking the purity is ongoing and there is an RFC PR that more or less tracks the work. It is quite problematic, though, to retrofit this feature into the existing language.
I would argue with that. At least in the code that I am writing, it is idiomatic as type alias is not a type definition and I prefer to give types to everything. An immediate bonus is that it helps the type checker give you precise types and provide error messages relative to the problem location, without requiring any type annotations. I, however, prefer to use records for such cases, mostly for syntactic convenience.