On Wednesday, 15 February 2023 at 07:23:39 UTC, thebluepandabear wrote:
>Why is the unit of encapsulation the module though? Makes no sense.
What is the purpose of encapsulation? To keep the implementation details hidden behind the public API, such that changing the implementation doesn't change the API.
Consider this:
module gfx;
struct Point {
private int x, y;
this(int x, int y)
{
this.x = x;
this.y = y;
}
void move(Point to) {
x = to.x;
y = to.y;
}
}
Here, move
is part of the public API. x
and y
are part of the implementation. Nothing outside the module can touch them.
Now this:
module gfx;
struct Point {
private int x, y;
this(int x, int y)
{
this.x = x;
this.y = y;
}
}
void move(ref Point from, Point to) {
from.x = to.x;
from.y = to.y;
}
From the perspective of the public API, nothing has changed. The following works in both cases:
Point p;
p.move(Point(10, 20));
writeln(p);
In both cases, the implementation is hidden behind the same public API.
If private were restricted to the class/struct, it would add anything more for encapsulation in D. In practical terms, if you are editing the gfx
module, you also have access to the implementation details of Point
.
Sure, if you have e.g., a special setter that does some extra work when a member variable is set, you want to ensure that only that setter is used to change the member variable. But that's true inside the class/struct as well.
I mean, just consider this:
class C {
private enum minX = -100;
private int _x;
void setX(int newX) { _x = newX > minX ? newX : minX }
void doSomething(State s) { setX(_x + s.val); }
}
vs. this:
class C {
private enum minX = -100;
private int _x;
void setX(int newX) { _x = newX > minX ? newX : minX }
}
void doSomething(C c, State s) { c.setX(c._x + s.val); }
Ideologically, they are not the same. In practical terms, they are. Whether the closing brace of the class declaration is before or after doSomething
matters not one bit. Yes, things can go wonky in a module that's many lines long and someone sets _x
from outside of the class. So what? The same is true for a class that's many lines long when someone adds a new method that directly sets _x
rather than going through the setter.
D's modules are intended to be used for grouping related constructs. Everything in the module is part of the same private implementation. If the constructs aren't related, then put them in separate modules. And there's still a solution for anyone with a strict ideological preference regarding related constructs: they can put their classes and structs in individual modules under a common package. package
protection can be used for cross-module access inside the package, and the entire set can be presented to the outside world as a single module with package.d
.
Our friend of many forum handles misses no opportunity to return to this putrid horse corpse to beat it some more, but the meaning of private isn't going to change. This is D's approach to encapsulation.