How do people deal with functors in functors?

I’m working with a codebase that involves a lot of functors, and a lot of interfaces (sometimes in files, following the trick from this post), and a lot of destructive substitution) (the with type _ := _ ), and it is really hard to figure out how all of these functors are built on top of one another.

Furthermore, since modules split in different files can’t both rely on one another (they would be recursive modules), some modules are split and constantly reimported.

I’m wondering if there’s a way to think about all of that in OCaml, or perhaps tools to aid on comprehending such a codebase.

My take:

  • The “locate” (jump-to-definition) feature of Merlin should be able to work through the various indirections to show you the definition site of each name of interest to you. Hopefully you can mostly ignore the module soup.
  • Like many advanced features, the module system has benefits and also costs in term of codebase complexity. It also has some well-known limitations (signature avoidance, etc.) that can get very annoying when you use it in very advanced ways. I don’t know the codebase you are referring to, but it might be that it is overusing modules.

(The bad news: it is never obvious how to avoid module-heavy code by using another approach – sometimes dirty tricks help. It is even less clear how to un-use module in a codebase that started relying on it heavily in its core design.)


Is there a way to see the list of indirections that merlin traverses?
Sometimes I want to step through that stack one module at a time. I don’t know how to do that.


Documentation generation tools (odoc) can sometimes help in understanding functor heavy codebases, to see all the values and types in their expanded form.
Unfortunately the current version of odoc loses some documentation, especially module level comments (at least in the version currently deployed on, see Doc strings get dropped on includes across compilation units. · Issue #878 · ocaml/odoc · GitHub so one still has to rely on reading .mli files to see all the documentation.
However if you just want to see the types and functions, and function documentation then odoc should be quite good at generating something more comprehensible than just the mli files where you’d have to trace through multiple levels of functors or module inclusions to figure out what you can actually call.

1 Like

XCode and some IDEs (or LSPs) have a feature called “caller hierarchy” where you can see a nested menu of all the parents (and their parents) of a function call. This is really magical when going through code, I wish we had that with ocaml-lsp : o