1. 12
  1.  

  2. 14

    This is (related to) one of the most frustrating parts of working with Kotlin for me: no package visibility.

    Kotlin chose to abandon Java’s package visibility modifiers, arguing that it didn’t really “protect” your code because anybody else could trivially write code under the same package name and then see all of your package-private code.

    But that’s missing the point. We don’t usually write “private” because we’re afraid of people seeing or using the code. We write it so that they don’t have to see the code. Having an interface that is as small as possible reduces the cognitive load of someone consuming your library/package/class.

    I feel like the cognitive load aspect is something not discussed as much.

    Aside: In Kotlin, the suggestion is to just use “modules” instead of packages if you want that kind of protection, since it offers “true” protection from consumers accessing the private parts of your sub-code. I hate that because it’s more effort to move pieces around between modules, to change the public/private interface of a module, etc.

    1. 3

      Historically, languages have been poor at dealing with levels of abstraction above the class. When they do deal with them, they often aren’t first class constructs. It’s a shame. One could argue that micro-services (and many other things like OSGi) came from the absence of these abstractions in languages. My sense is that language designers don’t want to commit to a deployment model. Sadly, protection loses too.

      1. 3

        Yes. It is a shame. I feel like Rust modules are pretty nice, but then, I also don’t mind Java’s packages at all, either.

        What I find uncomfortable is this recent trend of the language acknowledging the concept of a file when it comes to privacy/visibility (e.g., Swift and Kotlin). That just feels weird and wrong to me…

      2. 2

        Exactly! Every namespace is a precious resource to be maintained as neatly as possible.

        1. 2

          We write it so that they don’t have to see the code.

          I didn’t mention it in the article, but the “scissor test” is a concept that I like in regard to this. The idea is that if you were to print out a file of code on to paper, there should be a line that you could cut through with a pair of scissors that separates the interface from the implementation details. So if you want to use the class, you only need to read up until the scissor line, but if you want to understand how it works under the hood you can continue reading further. The scissor line is basically where private starts.

          1. 2

            Kotlin chose to abandon Java’s package visibility modifiers, arguing that it didn’t really “protect” your code because anybody else could trivially write code under the same package name and then see all of your package-private code.

            Interesting. My tendency would be to go the other way and eliminate private and protected, but keep package. Anything in the same package as a class that depends on implementation details of that class is easy to refactor at the same time as a change to my implementation, so I don’t gain anything much from private and protected that I don’t have from package.

            If you add a new class in my package, then you are effectively forking my package. That’s fine, it’s up to you to decide that the maintenance burden of doing so is worth it for the change that you want to make. If I refactor my package and break something that your class depends on, that’s your problem because you have a downstream fork of my package, not just something using the package.

            1. 1

              Kotlin, as you mentioned, has internal visibility to hide things between modules. I find it nicer than package-private since you don’t need to have one package with many classes inside (subpackages’ classes can’t access the package-private members of a class in a parent package)

              1. 2

                I forgot that Java has no concept of subpackages, which is also disappointing. Rust modules seem to be the winner, then, from my experience.

            2. 9

              Other reasons:

              • Interface clarity
              • Prevent invalid states
              • Specifically to take power away (principle of least privilege)
              1. 3

                There are many language features that are both documentation and enforcement. Private is like that. Anyone can come by later and change it to something more visible. There’s some wiggle room there.

                A friend once made an IDE plugin he called Thatcher which went through a Java project and made everything private that could be private. To me, it seemed like a great idea as long as people felt free to increase the visibility as their design changed.

                1. 2

                  That does sound like a good idea. It’s like dead code elimination, but for interfaces. Nice name, too.

                2. 3

                  I’ve always liked how Python does it: by convention a leading _ indicates that something is intended for internal use, and is not part of the public interface. But it’s there, and you can import it and use it if you need to. This has been useful for me on several occasions.

                  1. 2

                    Agreed. There are probably situations where it’s advantageous to enforce it strictly, but in my experience it’s perfectly sufficient to just make it obvious (with something like an underscore), with the added benefit that you still have a choice instead of the compiler taking choices away from you.

                  2. 1

                    “ Categorising code as either public or private is an attempt to reduce the costs of change by communicating stability.”

                    The purpose is to hide internal functionality from those seeking to use an abstraction. It isn’t to somehow indicate what is stable or not.

                    “It’s to reduce future maintenance costs by discouraging coupling to unstable dependencies.”

                    I think this is a possible consequence, not its purpose.

                    1. 1

                      The purpose is to hide internal functionality from those seeking to use an abstraction. It isn’t to somehow indicate what is stable or not.

                      Why hide internal functionality? Why not make it all public?

                      1. 1

                        Because objects have rights. And from that perspective, the unnecessary word is not private, but public; why should a language ever have a default behavior of breaking isolation and integrity? When we allow objects to keep to themselves and be left alone, then we are able to more fully notice when we take improper advantage of ambient authority.

                        1. 1

                          Well it depends on what one is modeling, but as an example consider the Abstract Data Type, like a stack or queue. They are defined in terms of their operations, not their internal representation. That representation together with supporting functionality is hidden by definition.

                          Alternatively, consider that you are modeling a phenomena using an object in an object-oriented language. That one can make requests that the object do something without necessarily knowing how it does it, is how one can model a complex process unimportant to the requestor. An elevator (modeled in software) might know how to move between floors, but how it does so is unimportant to the requestor.

                          This perhaps goes right back to the invention of the subroutine by Maurice Wilkes in the early 1950s. Being able to decompose problems into small reusable parts that hide their implementation, is necessary to create complex programs. The alternative of a very long procedure that merely has jump or goto statements is surely unattractive unless the problem is simple or trivial?

                          1. 1

                            Making things hidden (private) ensures that you don’t break someone else’s code with your internal changes. It’s a contract between you and your user – they agree to the fact that they won’t be able to use the private parts, while you agree to never break the public interface, while still having the ability to turn the internal details upside down if you so desire. As others have mentioned, it helps to reduce the mental load too: the user doesn’t need to read into every internal detail of your objects/modules, it’s enough for them to just be aware of the public bits.

                            1. 1

                              Yep, it’s to allow changes, and changes are a measure of stability. If it could be guaranteed not to change, then you wouldn’t really get any benefit from making it private. I was trying to show that to the original commenter with Socratic questioning, but they managed to dodge it expertly.

                        2. 1

                          In languages like Rust private components help organize, structure and deduplicate your internal unsafe logic, whilst providing a safe public interface to the user.