From what I can see, the main problem with internal compare to package private is that internal is tied to Gradle/Maven modules. This discourages developers from building small modules, as doing so affects compilation time and complexity of the overall project structure. Package private solves those problems, but introduces a lot of problems by itself. Instead, I propose that we look for alternative solutions. My suggestion would be to add a notion of modules within the language itself, that are more lightweight than what the build systems offer, but also more flexible than packages, since packages are restricted to a single namespace and a single directory file structure.
The “lightweight” module I prefer is the ability to mark a package as a module. This restriction is only enforced at compile time. Those members marked with internal will then be compiled to package-private members post-fixed with ugly module name to tell Kotlin it’s internal.
How about defining a scope which is not visible either from outside the package or from outside the module?
The Dylan language allows you to expose multiple different “modules” (named lists of types, functions, etc.) from each “library” (compilation unit), so you could have things exposed for general use from one module, and things for testing from another. Modules would also import things from other modules. There are no doubt other languages which do similar things.
It works well in terms of encapsulation options, but it forces you to explicitly list everything you want to export in a module file, separate from the definition of the thing, which is a pain to maintain.
I could imagine Kotlin having a default module for exporting public things, and @ExportFrom annotations on other things to export them from one or more other modules. But how you would represent that cross-platform behind the scenes, I don’t know.
I’d heard about jigsaw but not looked into it yet. It seems like a great thing but only operates at the class level. I don’t see an obvious way to export different fields or methods of a class via different modules, which some might want for testing. Perhaps one could make a class method package-scope, have it implement a package-scope interface from a sub-package, then export the sub-package from a separate module and/or export it only to test modules? Not sure that works, though.
Of course, you can usually refactor to avoid having to call some otherwise private/protected methods for testing.
Actually, java modules work at the package level, not the class level.
Sorry, I wasn’t clear. I meant that the finest possible level of granularity I could see as being possible would be the class level, if you had one class in a package. But I didn’t see a way to expose a class and only some of its methods from a package.
Custom scopes might be interesting. Like having an extension scope for plugins and a user scope for regular users. Perhaps they don’t need to be public either (internal, but explicit). One limitation would be the need to state the actual scope on the use side per file (or at build/gradle level)
So what’s the current stance on this from the Jetbrains team?