And how would that work with or without mli-files? Or partial compilation?
Inference is resolved at the module level. That means all let-bindings in a module are type-inferred when the module is compiled. So, compiling a module infers a module signature. If the module does not have an .mli
file, then the inferred module signature becomes the actual one. If the module does have an .mli
file, then the inferred signature is checked against the signature given in the .mli
file. If it conforms, then typechecking suceeds. If not, typechecking fails and a module type error is printed.
Partial compilation is done at the granularity of modules, so the above works.
Right, thanks. I was a bit confused about calling it “global type-inference” in some places. But global = file-local, then.
It’s even less than that, though ? Inference happens at each “let” binding (in structs) one-by-one in file-order, right? [Though I don’t know how that interacts with modules defined via let module M = struct ... end in ...
]
It only appears that inference is happening at the module-level, b/c each let is given a most-general type, so there’s never any need to generalize further due to uses of the names defined in that let, that might appear later on in the file.
Or at least, that’s how I remember things … I could be mistaken.
Narrator: he was indeed mistaken.
if you say
let x = ref []
let y = 1 :: !x
then x
would be typed as int list ref
. So I don’t think inference is limited to the scope of a single let
binding, at least for the weak type variable case.
Oh ha! I had forgotten about that! I stand corrected!
Yes, OCaml decouples compilation of modules from each other. I collected some thoughts on that here: OCaml interface files–hero or menace? - DEV Community 👩‍💻👨‍💻
Perhaps it is worth mentioning that when compiling a module, OCaml compiler needs the .cmi
files of all the dependencies (so the types in these modules have already been inferred, and possibly checked and constrained against their respective .mli
if any).