Kotlin to support package protected visibility

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.

3 Likes

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.

You have jigsaw for that. Kotlin does not fully support it yet, but it will.

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)

2 Likes

So what’s the current stance on this from the Jetbrains team?

Companion object can access private constructor

Any roadmap or progress in this direction? I’d love to not have my project namespace polluted with dozens of classes that could be easily package isolated.

8 Likes

Feature request: https://youtrack.jetbrains.net/issue/KT-29227

2 Likes

private does not provide any real encapsulation. Any other module in the system can use reflection and get full access to its internals. Can this mean private can also be omitted?

4 Likes

No longer true for Java 9+ where it can be actually restricted. But you don’t write access specifiers to protect against people who want access at any cost, you do it to protect it against unintended access. Modifiers are not a security measure.

4 Likes

That’s actually also his point. He argues that the same can be replied to @yole, hidden in a somewhat sarcastic tone.

1 Like

I have just recently switched from Java to Kotlin for microsevice implementation. I like the language but the absence of package private visibility is what discourages me the most so far.

I’m surprised no one yet mentioned anything about two major practices for structuring your code: package by layer vs package by feature. This article describes the subject really well: Java Practices->Package by feature, not layer

So what you Kotlin team did is made all the people who use package by feature to structure the code quite unhappy.

Basically i want my package to become my second most primitive module unit after the class itself. Package scope (visibility) is an essential unit for code modularization and you have thrown it away making package a mere unit for structuring physical files.

The thing is, these modifiers are visibility modifiers. Not access modifiers. They don’t provide encapsulation - there’s no real encapsulation in OOP. They let you constraint the code visibility. And we are using that to express ourselves. Not to protect our code from being accessed. Yes you can access my code and i’m ok with that because you can only do that in an unnatural way implying that you shouldn’t be doing that in the first place.

  • This is not about encapsulation - this is about modularization by means of visibility constraints
  • This is about the design, about being able to draw the line while structuring your program
  • This is the second most simple and one of the most important methods for modularization in Java, enforced by the language designers by default
  • No package scoping in the language forces us to make all of the package private classes private staffed inside the single file which is just bad
  • No package scoping in the language provokes the pollution of the classpath rendering code autocompletion useless
  • internal visibility modifier is useless when you work on the same codebase and has no actual value in this context thus can’t be considered as package private replacement
  • The larger the codebase the more of the problem absence of the package scoping really is

I don’t need to protect my code. I want to express myself. I want to hide implementation details in my package to make my design and intentions clear to others and to myself when i revisit the codebase.
With package scoping i can design the module around the package exposing only it’s public API and leverage DI framework to actually wire the “package modules”. If my package grows i separate it into the actual module or even microservice of it’s own. When i use DDD with “package by feature” i can naturally implement everything related to one aggregate root within single package and later make that a separate microservice without much effort.

Please, Kotlin team, reconsider this and incorporate the package visibility scope into the language.

I couldn’t agree more.

package private was very cleverly chosen to be the default scope. And Kotlin throws it away making everything wide-open by default. And internal is the same “wide-open” in the context of a single code base. Most of the classes we write are an implementation details related to the package we are in so public isn’t suitable as the default scope, is it?

15 Likes

@eugene, What you describe is easily achieved by creating a nested “internal” package within each feature package.

It allows package by feature and expresses the intended access of the class within the app or library. One feature accessing another feature’s internal details is “unnatural” since it has to reach into the other feature’s internal package.

Note: I use package by feature in this way and I am quite happy.

@nickallendev, you invented your own convention with the dedicated nested package, correct?
While i see how it helps to address part of the problem in question, i still can’t see it as a replacement for proper visibility scope control: granted a medium to large codebase one could easily use your internal classes without the second thought on a couple of successful autocompletion attempts. And validating the class packages against the convention on every autocompletion would be so inconvenient, one could consider that a waste of time.

In the end, i don’t want to ask everyone to follow my convention and not use classes from the “internal” package, i want to have a way to enforce that by means of the language. Furthermore, i want to reduce the set of wide-openly visible types and the corresponding mental overhead.

Constraints liberate.

2 Likes

@eugene

you invented your own convention with the dedicated nested package

Not exactly, my team shamelessly stole this particular convention from other libraries:
https://github.com/ReactiveX/RxJava/tree/2.x/src/main/java/io/reactivex/internal
https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-core/common/src/internal
https://github.com/square/moshi/tree/master/moshi/src/main/java/com/squareup/moshi/internal

one could easily use your internal classes without the second thought on a couple of successful autocompletion attempts

It’s quite easy for auto-complete to expose classes of an external library not even within the same project in Java.

i don’t want to ask everyone to follow my convention

I don’t advise anybody to randomly start using their own conventions within a team and demand others to adhere. Do what works for your team.

Using a package naming pattern is a simple way to express intentions. It can be especially helpful when using a tool (like an annotation processor) that requires public classes. It is not the only way to express such intentions and it is definitely not a replacement for modularization.

If a project is too large then you also have to deal with other problems like long build times and commit histories that are cluttered. While package private can de-clutter your autocomplete, it is not a solution that fully replaces modularizing your project. If you do modularize your project, then Kotlin’s internal visibility will work quite well.

Not if they properly used visibility modifiers.

2 Likes