Basically, would you rather want everything in a class/module/struct/whatever to be inaccessible by anything else unless you give a keyword saying it’s public (like Rust) or have everything accessible unless you make it private (like Ruby and Crystal)? Why?

Also, what do you think of languages that make you write public or private for every member, or ones that inherit the default state from whatever the parent member is?

  • @ttmrichter
    link
    12
    edit-2
    2 years ago

    Only make public that which you name publicly. That’s the model I like best.

    Why?

    Philosophy. Your public API is an important part of your system-level documentation. It is what other people look at when building onto your system or interacting with it. Flagging intent (“you are intended to use this”) is better than flagging non-intent (“you are not intended to use this”).

    Consider these notional functions in a module:

    • DoSomethingSafe()
    • DoSomethingEvenSafer()
    • DoSomethingIncrediblyDangerous()
    • DoSomethingEvenMoreDangerous()

    Now if I have to declare publics…

    • public DoSomethingSafe()
    • public DoSomethingEvenSafer()
    • DoSomethingIncrediblyDangerous()
    • DoSomethingEvenMoreDangerous()

    …you’re a single grep away from getting the full public API of the module. If I happen to forget a public keyword…

    • public DoSomethingSafe()
    • DoSomethingEvenSafer()
    • DoSomethingIncrediblyDangerousAsPartOfImplementationDetailsOfTheFirstTwo()
    • DoSomethingEvenMoreDangerous()

    …the compiler will let me know in no uncertain terms that I screwed up when I write my tests. (You write tests, right?) The fix is typing public and all’s good. At no point does a client accidentally access a dangerous function thinking it’s intended for public use.

    Now compare and contrast with this:

    • DoSomethingSafe()
    • DoSomethingEvenSafer()
    • private DoSomethingIncrediblyDangerous()
    • private DoSomethingEvenMoreDangerous()

    Now first problem: it’s hard to grep for the public interface. I can grep easily for the parts I’m not supposed to consider, but the parts I should be using? Not so simple. That alone is a deal-breaker for me, though good tooling can mitigate this. My very firm opinion is that if you realistically need an IDE and advanced tooling to use a language, it’s a bad language.

    But now for a bigger problem. If I forget a private tag…

    • DoSomethingSafe()
    • DoSomethingEvenSafer()
    • private DoSomethingIncrediblyDangerous()
    • DoSomethingEvenMoreDangerous()

    Now a dangerous function has been exposed completely by accident, and a user will, inevitably, use it thinking it’s intended to be part of the user application interface. This has the obvious problem of a dangerous interface being used, but the more subtle problem, too, of this possibly becoming entrenched technique and now I can’t change the implementation without breaking a lot of existing code. Adding a new public interface, after all, rarely causes serious problems, but changing one, or deleting one is a headache in software release management.

    Now there’s a final reason I prefer declaring my publics, not my privates. (You shouldn’t be looking at my privates, dammit, you perv!) My personal style of writing modules (or objects or …) is to have a small number of public interface functions and a large number of private implementation helpers as building blocks.

    Here’s public declarations:

    • public F1()
    • public F2()
    • H1()
    • H2()
    • H3()
    • H4()
    • H5()
    • H6()
    • H7()
    • H8()
    • H9()

    Here’s private declarations:

    • F1()
    • F2()
    • private H1()
    • private H2()
    • private H3()
    • private H4()
    • private H5()
    • private H6()
    • private H7()
    • private H8()
    • private H9()

    Guess which one involves more typing…

    • @sacredbirdman
      link
      3
      edit-2
      2 years ago

      And here’s one that defaults to nothing but involves less typing:

      public:
          F1()
          F2()
      private:
          H1()
          H2()
          H3()
          H4()
          H5()
          H6()
          H7()
          H8()
          H9()
      

      It’s quite easy to awk public functions from that too.

      • @ttmrichter
        link
        12 years ago

        That’s not bad, but for the fact that the part that tells you which is public and which is private is long-removed from the declaration/definition. I wouldn’t hate this, but it’s not my favourite. Personally I just don’t really grok any “characters typed” arguments. If you’re doing software right you read a whole lot more often than write, and anything that makes reading easier is better, IMO.

        • @sacredbirdman
          link
          12 years ago

          I’m a bit confused… I thought you just said that your second example involved more typing. Anyway, I don’t care much for that characters typed argument either… but I do care about readability and “noisy” syntax or repetition tends to make reading/skimming harder.

          • @ttmrichter
            link
            12 years ago

            Oh, that’s because I saw other responses that were already talking the typing shibboleth. I should have made that clearer. My bad.

  • ☆ Yσɠƚԋσʂ ☆
    link
    52 years ago

    I think it depends on whether the language defaults to mutability or immutability. In a language defaulting to mutable data it’s important to keep tight control over data access and what’s exposed.

    In a language where immutability is the default this problem largely goes away since changes only affect the local context. The only case where you might want to make something private would be to hide implementation details. If something isn’t part of the public API then it can be changed later on without becoming a breaking change for the users of the API.

  • Ephera
    link
    32 years ago

    To me, it comes down to: Is it dumb data or not?

    If it is dumb data that gets passed through my program, then everything should be public (and ideally immutable).

    If it’s not dumb data, but rather the state of a piece of logic (typical in OOP), then I want everything private.

    If the language doesn’t distinguish between those, then I prefer private by default, because it’s hard to forget making it public, if you actually need to access it from the outside.

  • @pancake
    link
    22 years ago

    Everything public wouldn’t be a problem for readability I believe, as I give variables really verbose names. It would be slightly harder to write good code, but still. Everything private would force me to go full OOP and it would be better 80% of the time, but the other 20% would make me want to die a lot.

  • DessalinesA
    link
    22 years ago

    For Object oriented / class-focused languages, I kinda like default public members like Ruby or typescript does. You’re making something where you’re going to use its functionality elsewhere, so it saves some characters to have default public.

    Both work tho, and rust isn’t too bothersome because the compiler will tell you when you’re missing the pub keyword.

  • @blank_sl8
    link
    -52 years ago

    Private members should not exist. A language or framework should never prevent you from doing something, just discourage it (eg, by prefixing the name with an underscore)

    • @ttmrichter
      link
      52 years ago

      This kind of attitude is precisely why software is in such a crap state.

    • Ephera
      link
      52 years ago

      The point is not to actually prevent you from accessing that member. Most member accesses are within your own code, so you can just edit that private member to be public.

      But by performing this edit, you document that someone is accessing that value from the outside and therefore changing its value can have unexpected consequences.

      This isn’t the case for libraries, because you can’t edit that code quite as easily, but at least personally, I’ve seldomly had enough confidence to just access random values in a library, and you can usually side-step these ‘protections’ by e.g. reflection, if you truly need access.