Ada-Rapporteur-Group / User-Community-Input

Ada User Community Input Working Group - Github Mirror Prototype
26 stars 1 forks source link

Getters and setters for private types required even inside body. #80

Open Blady-Com opened 6 months ago

Blady-Com commented 6 months ago

User access to fields of a record type declared with private within a package requires the declaration of getters and setters. Moreover, within the package's body, there is no necessity: access can be done directly. It’s the same with package's children. If the notion of getters and setters aims to ensure better control of data, there is no obligation to do so in a body. I propose an extension to increase this control of data by specifically identifying the fields requiring this control, for example with the keyword private:

type Record_With_Protected_Fields is record
    Non_Protected_Field_A : Integer;
    Protected_Field_B : private Integer;
    Protected_Field_C : private Integer;
end record;

Fields Protected_Field_B and Protected_Field_C can no longer be accessed directly even within the package body:

    function Field_B (R : Record_With_Protected_Fields) return Integer is
       begin
       return R.Protected_Field_B; -- illegal
       end;

    procedure Field_C (R : Record_With_Protected_Fields; V : Integer) is
       begin
       R.Protected_Field_C := V; -- illegal
       end;

Non_Protected_Field_A remains accessible:

    function Field_A (R : Record_With_Protected_Fields) return Integer is
       begin
       return R.Non_Protected_Field_A; -- legal
       end;

Well, you still need to have access to the fields Protected_Field_B and Protected_Field_C! I propose to add the same keyword:

    function Field_B (R : Record_With_Protected_Fields) return Integer is
       begin
       return private R.Protected_Field_B; -- legal
       end;

Likewise:

    procedure Field_C (R : Record_With_Protected_Fields; V: Integer) is
       begin
       private R.Protected_Field_C := V; -- legal
       end;

This way, even inside the body, the access to fields Protected_Field_B and Protected_Field_C will only be possible through Field_B and Field_C.

nholsti commented 5 months ago

I'm not in favor of this proposal, for two reasons.

Firstly, I think the usual assumption in package design is that the implementers of the package know how the private components should be managed. If using or setting private components involves rules and checks that would be cumbersome to apply at every use of such a component, the implementers can be expected to implement operations (what the OP calls getters/setters) for that, whether private to the body or visible via the package declaration, and to use them within the body where needed. This is similar to how invariants of a type are checked mainly in returns from calls of the "boundary" entities of the type, but are not checked within the bodies of such entities, and indeed can be violated in those bodies. Here the package declaration serves as the "boundary" of the package.

Secondly, I suspect that if this change is implemented, and the compiler complains of an illegal access to a "protected" component, most programmers will simply add the "private" keyword to the access and consider that to "fix" the problem. I've seen many programmers react to type-related compilation errors by just adding type conversions until the compiler is silent. So the end result will, in most cases, just add a lot of meaningless "private" keywords to the code.

Sometimes we should trust the programmer, even in Ada.

Blady-Com commented 5 months ago

Firstly, I think the usual assumption in package design is that the implementers of the package know how the private components should be managed. If using or setting private components involves rules and checks that would be cumbersome to apply at every use of such a component, the implementers can be expected to implement operations (what the OP calls getters/setters) for that, whether private to the body or visible via the package declaration, and to use them within the body where needed. This is similar to how invariants of a type are checked mainly in returns from calls of the "boundary" entities of the type, but are not checked within the bodies of such entities, and indeed can be violated in those bodies. Here the package declaration serves as the "boundary" of the package.

I understand that the body is the "den" of the programmer. However in case of debugging getter / setter eases tracking component changes. When the code is maintained by a large team, the compiler will help by checking if each component access is done by a getter / setter.

Secondly, I suspect that if this change is implemented, and the compiler complains of an illegal access to a "protected" component, most programmers will simply add the "private" keyword to the access and consider that to "fix" the problem. I've seen many programmers react to type-related compilation errors by just adding type conversions until the compiler is silent. So the end result will, in most cases, just add a lot of meaningless "private" keywords to the code.

Of course, this is a risk of drift. This risk may be mitigated by easily checking the use the private keyword.