At this point, you consider either adopting some naming convention for "private" exported fields or you merge all of these small modules into a single huge module representing the entire package.
The situation is made worse by the lack of support for cyclic imports, because this is an additional major reason to introduce such a types module.
There are other problems affected by the lack of package level visibility, but they might have other better solutions. For example, it's currently not possible to write unit test files that test the private routines defined in another module, but this may be better solved with one of the following options:
T1) Creating a new unit test framework that is able to collect test cases defined across your entire program (each module defines its own tests)
T2) Testing private routines is probably not a great practice anyway. Even the "private" helpers can be considered an API that lives in its own private module that has public exports.
Would it make sense to have an internal type, and a distinct public alias?
The implementation of the public functions use a private converter from the alias to the internal type.
Never tried it though.
Isn't it the case though, that this can be solved by not introducing a types module, and instead have smaller modules that implement the individual types in a more separate way, with a clean, orthogonal and complete API?
Incidentally, this approach encourages the creation of good unit tests as well, that at the same time:
You can work-around this by making all fields private and by introducing accessor templates, but at this point everything is so tedious that it's clear the language needs fixing.
Sounds like the perfect job for a macro ;)
removal of the "forwarding requirement"
oh, yes, please! forward declarations are so 90's! they can be a nice touch to optionally have, for example when exposing a documented public API, but mandatory??
How about something similar to child packages in Ada? [1] - where "sibling" modules could have visibility of each others, as well as their parents private parts.
[1]: https://en.wikibooks.org/wiki/Ada_Programming/Packages#Child_packages
Well, I've took some of the advice in this thread and created a macro module trying to automate the required symbol forwarding and template generation steps. It did survive its integration in our relatively large and complex codebase, but some new limitations were discovered along the way. You can find more information here:
https://github.com/zah/nim-package-visible-types
Otherwise, I think I need to explain better the genesis of the "types" module problem in Nim. Imagine you are writing a software package for managing a law practice. Naturally, you'll have a module called lawyer representing a single attorney. Since the lawyer must be able to plan his schedule, each instance of the Lawyer object will have a calendar with active CourtCase objects. Now let's move to the courtcase module. The CourtCase object will need to maintain a list of participating lawyers because it will need to notify them when a new session is scheduled for example. This creates a circular dependency in the type graph that can be solved currently only through merging the modules into a single law module or by introducing a law_types module. Anyone who believes that such circular type dependencies don't belong in a properly designed software is just deluding themselves.
So, any long term solution should either embrace the types modules as something natural in Nim that must be well-supported in all situations, or the cyclic imports should be able to handle cyclic type graphs spread over multiple modules. Single module tricks such as reorder are not sufficient, the lazy algorithms of the former noforward may work.
In case you are wondering why this isn't a problem for other languages, cyclic type definitions are usually fully supported for reference types (either transparently as in Java and C#, or with forward declarations as in C++).
Few answers to specific remarks:
Isn't it the case though, that this can be solved by not introducing a types module, and instead have smaller modules that implement the individual types in a more separate way, with a clean, orthogonal and complete API?
No, due to the reasons above.
I think some submodule idea combined with a removal of the "forwarding requirement" is the best way to do it but this needs an RFC.
It's not clear to me if these submodules are able to create cyclic type definitions spread over multiple modules. How would that work? How would you solve the visibility conflicts that started this thread.
I'd like to suggest an alternative, simpler proposal: see https://forum.nim-lang.org/t/4296#26721
Private imports would create their own set of conflicting requirements for the goals of incremental compilation. You wouldn't be able to cleanly separate the public interface of a module from its private components that should not trigger a rebuild upon changing.
Restrictions about cyclic type defs and the "forwarding requirement" are currently not in line with with nim's otherwise great flexibility, IMHO.
A "package-visibility/friendship" feature is probably needed too (but as yglukhov said) not as a replacement of aforementioned features :)