TITLE: Extending D's support for object-oriented design with private(this)
NOTE: Only civil and well-informed discourse on this idea is welcome.
DIP forum guidelines are here: https://github.com/dlang/DIPs/blob/master/docs/guidelines-forums.md
BEGIN:
This DIP idea, relates to extending D's support for object-oriented design - by allowing private visibility to be attached to a class member.
The proposal creates no new limitations and no code breakage.
BACKGROUND:
The class type is the core building-block of class-based object-oriented design.
The facility for abstract, structured reasoning about a class type comes from its support for encapsulation.
D provides a class type, which in turn allows D to support class-based object-oriented design, with encapsulation, inheritance, and polymorphism.
see: https://dlang.org/spec/class.html
However, D's minimal encapsulation unit is actually a module, not a class.
That is, class private members are actually private module members, not private class members.
Although private class members cannot be interacted with from another module, they can be interacted with using code inside the same module (but outside of that class).
That's because (in D) the module, not the class, is the encapsulation unit.
Example (code in the same module - but outside of the class definition - accessing a private class member):
// --
module m;
class C
{
private int _count; // visibilty of this member extends to the entire module.
}
unittest
{
C c = new C();
c._count = 42;
}
// ----
D also provides another form of encapsulation for 'a set of modules', when those modules all belong to the same package.
As such, a private class member cannot be interacted with from other modules that don't belong to the same package, but can be interacted with using code inside any of
the modules belonging to that package.
D's insistance (and apparent zealous protection) for having the module as the minimal encapsulation unit, is inconsistent with object-oriented design, where the class is itself a unit of encapsulation.
This has the following consequences:
(1) It makes it difficult to reason about a class definition in a module, when there is other code in the module, as all other code in the module is in essence a part of the class definition.
(2) It allows for accidental, unintended use of a private member by other code in the module, including unittests.
// --
module m;
class C
{
private int _count; // visibilty of this member extends to the entire module.
public void setCount(int c) { _count = c; }
}
unittest
{
C c = new C();
c._count = 42; // Actually, this is a mistake, and the programmer should be testing the public method setCount(..)
}
// ----
(3) One can only 'simulate' class-based encpasulation in D, by putting each class in its own module. This is, apparently, what is recommended whenever this topic is raised in the forums.
Both (1) (2) (3) can be fully resolved by adding private(this) functionality, as demonstrated here:
// --
module m;
class C
{
private(this) int _count; // visibilty of this member is contained within this type
}
unittest
{
C c = new C();
c._count = 42; // Error: data member _count is an inaccessible (private)property of type `m.C`
}
// ----