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.