> But .mli files do not help with the "no types in the source code" problém
It partially helps since it forces you to have types where they matters most: exported functions
> And I did not experience any advantage of separate signature files so far,
100kLoc is already quite big! I'm starting to think I'm an outlier since a lot of people don't see the benefits :)
For me, it helps because I really don't want to see the implementation when I use an API. If I need to look at the implementation, it means the interface isn't well specified. All I need should be in the interface: types, docs, (abstract) types. And no more.
Typically, an .ml file will have more than what is exported, types won't be abstract but will have a concrete implementation, and type signatures may be missing. How would it feels like to use list if only https://github.com/ocaml/ocaml/blob/trunk/stdlib/list.ml was available, instead of https://github.com/ocaml/ocaml/blob/trunk/stdlib/list.mli? and that's not the most compelling example since it's a simple module where the main type isn't abstract. Generally speaking, giving up mli means giving up on encapsulation which is one of the primary tools to deal with large programs.
Haskell forces you to say what is exported from a module, but the module user can only see the names. To see the signatures, you need to rely on generated doc or look it up on the code.
Arguably, since OCaml has includes, it suffers from the same problem, your ".mli" may have tons of include and it becomes harder to see what's exported without an external tool