Conventions around Internal modules

(Disclaimer: I’ve used Haskell much more than OCaml.)

In the Haskell community, there is typically a convention that packages expose the “internals” (i.e. data constructors, special functions) via an Internal module, so other packages can rely on it if they wish to do so, at the cost of more frequent version bumps (because Internal modules don’t follow the versioning policy). Recently, there was some discussion on the r/haskell subreddit about this topic, prompted by a blog post which suggested separating out Internal modules into a separate package.

In that thread, multiple library authors have provided anecdotes about how a library not exposing Internal modules proved to be a troublesome experience for them.

In my experience using OCaml, my perspective has been that most package authors do not expose internal details.

  1. Do you think this is a fair characterization of the OCaml ecosystem?
  2. Have you found this to be problematic at any point while developing your own libraries?
  3. Have you developed libraries in Haskell? Do you feel there is a noticeable difference between the two communities on this subject, and if yes, any ideas why this might be the case?

I ask this because – on one hand, having abstract types seems pretty fundamental to the notion of encapsulation/“programming against interfaces”. However, it seems that in some cases, it is too limiting for package authors downstream, which feels inelegant (for the lack of a better word).

Superficially, it seems that the same problems that arise in Haskell should arise in OCaml too (or conversely, the lack of problems in OCaml should imply a lack of problems in Haskell), since the whole issue revolves around abstract types. I can see a case being made of exposing Internals of Haskell’s text and vector libraries to leverage stream fusion, which doesn’t carry over to OCaml, but apart from that, I don’t see an obvious explanation for the difference.