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@lemmy.ml
    link
    fedilink
    arrow-up
    12
    ·
    edit-2
    3 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()

    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@lemmy.ml
      link
      fedilink
      arrow-up
      3
      ·
      edit-2
      3 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@lemmy.ml
        link
        fedilink
        arrow-up
        1
        ·
        3 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@lemmy.ml
          link
          fedilink
          arrow-up
          1
          ·
          3 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.

  • Echedenyan@lemmy.ml
    link
    fedilink
    arrow-up
    6
    ·
    3 years ago

    I think that the visibility identifier should be forced by default and not allowed to write something without it.

    For me, it is confusing having to remember that state in every different programming language.

  • ☆ Yσɠƚԋσʂ ☆@lemmy.ml
    link
    fedilink
    arrow-up
    5
    ·
    3 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.

  • Dessalines@lemmy.ml
    link
    fedilink
    arrow-up
    2
    ·
    3 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.

  • Ephera@lemmy.ml
    link
    fedilink
    arrow-up
    2
    arrow-down
    1
    ·
    3 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@lemmy.ml
    link
    fedilink
    arrow-up
    1
    ·
    3 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.