April 27
On 27/04/2024 11:50 AM, NotYouAgain wrote:
> On Friday, 26 April 2024 at 16:38:54 UTC, Steven Schveighoffer wrote:
>> ...
>> module m;
>>
>> class C
>> {
>>    private(this) int _count;
>>    public void setCount(int c) { _count = c; }
>>
>>    unittest
>>    {
>>       C c = new C();
>>       c._count = 42; // oops, this is still allowed
>>    }
>> }
>> ```
> 
> There is no 'oops' there.
> 
> The unittest is a member of the class!
> 
> It the unittest outside of the class that should not be allowed to compile.

Based on the fact that a unittest block does not have a this pointer, Steven is correct. It isn't part of the scope.

If you want this you have to be specific that it is included (and yes this is an example of why a DIP is needed to know what your assumptions are).
April 27
On Saturday, 27 April 2024 at 01:41:54 UTC, Jonathan M Davis wrote:
>..
> ...
> - Jonathan M Davis

As usual, the only solution you can provide to the problems that I've very clear outline, are put the class in it's own module.

That is the only solution to the problems.

You say that all that D needs to do.

I say, no, make it easier for me to solve my problems.

A class already has a boundary outside of a module.

Why you so determined to ensure a programmer never has the option of explicately declaring that boundary to other code in the class. I just don't get it. I can only presume you don't to oop, or you put every class in its own module, and every unittest for that class its own module as well.

Well, no. There is an easier way -> private(this).

No impact at all on existing code as far as I can tell. It's purely optin.

But I think you're determined to think the same way - in the same way that you tell me that I'm determined to think the same way ;

I've presented a number of examples now, where private(this) solves the problem identified.

You're solution is one class per module, no unittest in the module either.

No. I don't accept this as a good solution to the problems I've indentified.

April 27
On Saturday, 27 April 2024 at 02:03:35 UTC, Richard (Rikki) Andrew Cattermole wrote:
> On 27/04/2024 11:50 AM, NotYouAgain wrote:
>> On Friday, 26 April 2024 at 16:38:54 UTC, Steven Schveighoffer wrote:
>>> ...
>>> module m;
>>>
>>> class C
>>> {
>>>    private(this) int _count;
>>>    public void setCount(int c) { _count = c; }
>>>
>>>    unittest
>>>    {
>>>       C c = new C();
>>>       c._count = 42; // oops, this is still allowed
>>>    }
>>> }
>>> ```
>> 
>> There is no 'oops' there.
>> 
>> The unittest is a member of the class!
>> 
>> It the unittest outside of the class that should not be allowed to compile.
>
> Based on the fact that a unittest block does not have a this pointer, Steven is correct. It isn't part of the scope.
>
> If you want this you have to be specific that it is included (and yes this is an example of why a DIP is needed to know what your assumptions are).

No I don't understand.

The unittest is a special function being declared inside the scope of the class type. I would entirely expect it to have access to all private members of that type.

When the unittest is declared outside the scope of the function, I would expect it to NOT have access to the private(this) members of the type.

That's exactly what I expect and exactly how it works in OpenD.

I presume it's a static function? Hence no this pointer? (I really don't know how its implemented, nor do I care. I can only care that it works as I expect it to work in OpenD.
April 27
On Saturday, 27 April 2024 at 02:03:35 UTC, Richard (Rikki) Andrew Cattermole wrote:
>
> If you want this you have to be specific that it is included (and yes this is an example of why a DIP is needed to know what your assumptions are).

But I prefer to discuss this first, as there are things I cleary do not know. Hence the discussion.

The DIP process should incorporate what I've learned in the discusion, in order for the DIP to be worthy of the time and attention of those responsible for approving it.

At the moment, the discusion is not revealing anything I don't alread know.

That is, people 'seem' to accept that there are problems, by insisting that one just puts the class in a separate module, and a unittest for that class in yet another separate module. This discussion is about how to avoid putting the completely unnessary burden on programmers.

I know. How about providnig the solution in the language instead?

That's what's being discussed.

April 27
On Saturday, 27 April 2024 at 01:41:54 UTC, Jonathan M Davis wrote:
>
> Arguably, if someone feels the need for something like private(this), they're either doing something wrong, or they want it simply on the principle that types should be able to prevent all code that's external to them from accessing their private data. It really isn't an issue in practice, and if you really want to ensure that code can't access a type's private data, the workaround is simple.
>

I'm not doing anything wrong!

I'm declaring a user-defined class type, and I want it to have private members, that I don't want to be accessed outside of the scope in which the class is being defined.

How am I doing anything wrong here?

The class type can already prevent access to its private members - but only to code outside the module. Are you saying this isn't necessary? Just make it all public and no problems will arise? Well, problems WILL arise. Just as problems 'can' arise with code inside the module.

I'm trying to provide a strong type here, but D won't let me. It has no mechanism to do that, as it doesn't consider a class type to be a true type (but nontheless, declares itself to support OO programming).

To code 'outside' the module, I can present the class as a true type - 'private'.

To code 'inside' the module, I cannot. It simply cannot be done. Inside the module, 'private' means nothing.

The only solution you and others put forward, in order to ensure private really is private in a module, is to ensure no other code is in the module.

Really?!? You may as well tell me to f$$$ off. Cause they both mean the same to me.

Also, I'm not insisting that private be changed, only that private(this) be added.

*Both* have their use. In OpenD, **I** can choose between private and private(this). That's exactly what I would expect from any language I chose to program in.

Why would anyone *insist* on preventing me from doing that?

You cannot reason around a class type in D, when other code is in the module.
You have to take all that other code into account - and there lies the problem. The compiler will never know, and a code revievwer will have to review all code in the module, just to understand the intent in the desing of the class.. if they're lucky.

From the code in the class-type, I should be able to extract a proof or its correct usage.

In D, a proof is not possible when there is other code in the module.

And 'the workaround' is not simple!

I want to make it simple, by integrating into D, an optional feature (that already exists in OpenD, Swift, C++, Java, C# ......), so that the programmer can make the class contain 'proof-carrying code' - code that explicately declares how this type can be used.

private(this) is what would make it simple. My motivations could not be clearer.

April 27
On Friday, April 26, 2024 11:44:15 PM MDT NotYouAgain via dip.ideas wrote:
> On Saturday, 27 April 2024 at 01:41:54 UTC, Jonathan M Davis
>
> wrote:
> >..
> >
> > ...
> > - Jonathan M Davis
>
> As usual, the only solution you can provide to the problems that I've very clear outline, are put the class in it's own module.
>
> That is the only solution to the problems.
>
> You say that all that D needs to do.
>
> I say, no, make it easier for me to solve my problems.
>
> A class already has a boundary outside of a module.
>
> Why you so determined to ensure a programmer never has the option of explicately declaring that boundary to other code in the class. I just don't get it. I can only presume you don't to oop, or you put every class in its own module, and every unittest for that class its own module as well.
>
> Well, no. There is an easier way -> private(this).
>
> No impact at all on existing code as far as I can tell. It's purely optin.
>
> But I think you're determined to think the same way - in the same way that you tell me that I'm determined to think the same way ;
>
> I've presented a number of examples now, where private(this)
> solves the problem identified.
>
> You're solution is one class per module, no unittest in the module either.
>
> No. I don't accept this as a good solution to the problems I've indentified.

No. I simply don't buy that any of the issues that private(this) is trying to solve are problems in practice.

I have never found it to be difficult to reason about a class or struct just because there's other code in the module which could access the private members of that class or struct. And accessing private members by accident is not something that happens often if ever in my experience. If anything, it's usually a complete non-issue even if the code does access private members, because it's all code under the control of the maintainers of the module and has no effect on the public API.

And in plenty of cases (especially unit tests), it's actually desirable that other code within the module be able to access the private members. The whole point is to prevent code from outside the module from accessing the private data, and it usually doesn't matter if other code within the module does. And if it is actually a problem, then you can put the code in another module, much as that's obviously not the answer that you want. But in my experience, I simply don't have problems with code accessing private data when I don't want it to. Maybe that's in part due to the fact that I typically name private members differently from public ones by prepending them with an underscore, so it's very obvious which I'm using, making it rather hard to use the wrong one by accident, but I simply don't run into problems where having member variables be private to a module instead of to the class or struct is the cause.

So, I don't disagree with you that private(this) solves the issues that you're worried about. I disagree that the issues are significant enough that a language change is required.

I routinely put several types and functions within a module and put the unit tests in the same module right next to the code that they're testing, and I don't run into issues with private as a result of that. Plenty of other D developers have the same experience. So, from my perspective, you're trying to solve a problem that isn't even a problem in the first place.

In addition, mucking around with the implementation for private will almost certainly result in bugs added to the compiler, because that happens pretty much every time that the visibility attributes get mucked with. It also raises issues of what happens with __traits(getVisibility, ...), and it would presumably mean adding another string to the list, making it yet another thing that code doing type introspection potentially has to take into account. I'd rather not have to deal with that either.

Now, you clearly feel that there's a problem that needs solving and that private(this) will solve it. Some folks agree with you. And there's nothing wrong with bringing up the idea and trying to talk Walter into it. But I don't personally think that there's a problem that needs solving here or that adding the extra complication to the language that it would entail would be worth it. So, that's really our disagreement here rather than whether private(this) solves what you're trying to solve.

I'm also quite sure that unless something you say manages to change Walter's mind, he will have a similar opinion to mine, because that has consistently been his opinion on this topic for years now. But we'll see what he says when he voices his opinion about your specific proposal. Much as I expect him to reject it, I would say that your odds are probably slightly better with adding private(this) than they would be with trying to get him to change what private means.

- Jonathan M Davis



April 27
On Saturday, 27 April 2024 at 06:48:47 UTC, Jonathan M Davis wrote:
> ..
> ...
> ... So, from my perspective, you're trying to solve a problem that isn't even a problem in the first place.

Mmm. This is where you don't understand my motivation.

I'm not saying that it's causing widespread problems and that it needs to be addressed.

I'm saying, I want to prevent the problem from even occuring.

Almost any newcomer to D, will experience this problem.

There is no solution to them, other than to 'be more careful', or put the class in a module by itself, and ensure any unittest testing it, is also in another separate module. In that is solution being proposed to 'prevent' the problem'.

My proposal is more likely be used, as it's so much easier.

You present the hard solution to preventing the problems, I present the easy solution.

If I understand correctly, that seems to be where we differ.

You saying -> be careful not to do it so that it doesn't cause problems, or put use separate modules for everything.

Me saying -> use private(this)

A class is 'a type' of a particular thing, describing its properties and how it is to be interacted with, just like anyother type. If people come to D with that mindset, they will make mistakes, just like most newcomers make that mistake. Forcing them to think of the class as a subtype of the module, is not going to work. They'll just go elsewhere (which may well me the desriable outcome for some ;-)

April 27
On Saturday, 27 April 2024 at 06:48:47 UTC, Jonathan M Davis wrote:
>
> ...

What is your solution to the problem below?

Let me guess:
(1) don't make the mistake of bypassing the public interface in the unittest.
(2) put the unittest in a separate module.

My solution:
(1) make x and y private(this) and you cannot make a mistake, since the compiler will not let you make that mistake.

// ---

module m;
@safe:

class C
{
    private int x, y;

    invariant()
    {
        assert(x == y);
    }

    void modifyX(int val)
    {
        x = val;
    }

    void modifyY(int val)
    {
        y = val;
    }
}

unittest
{
    C c = new C();
    c.x = 10; // incorrect use of the type.
}
// ---
April 27
On Saturday, April 27, 2024 1:12:16 AM MDT NotYouAgain via dip.ideas wrote:
> On Saturday, 27 April 2024 at 06:48:47 UTC, Jonathan M Davis
>
> wrote:
> > ..
> > ...
> > ... So, from my perspective, you're trying to solve a problem
> > that isn't even a problem in the first place.
>
> Mmm. This is where you don't understand my motivation.
>
> I'm not saying that it's causing widespread problems and that it needs to be addressed.
>
> I'm saying, I want to prevent the problem from even occuring.
>
> Almost any newcomer to D, will experience this problem.
>
> There is no solution to them, other than to 'be more careful', or put the class in a module by itself, and ensure any unittest testing it, is also in another separate module. In that is solution being proposed to 'prevent' the problem'.
>
> My proposal is more likely be used, as it's so much easier.
>
> You present the hard solution to preventing the problems, I present the easy solution.
>
> If I understand correctly, that seems to be where we differ.
>
> You saying -> be careful not to do it so that it doesn't cause problems, or put use separate modules for everything.
>
> Me saying -> use private(this)
>
> A class is 'a type' of a particular thing, describing its properties and how it is to be interacted with, just like anyother type. If people come to D with that mindset, they will make mistakes, just like most newcomers make that mistake. Forcing them to think of the class as a subtype of the module, is not going to work. They'll just go elsewhere (which may well me the desriable outcome for some ;-)

What I'm saying is that there isn't actually a problem that needs solving. It's a theoretical issue and not one in reality. Programmers routinely program in D without running into any issues with private. Some of them even think that private is private to the class or struct and are surprised years later to find out that it isn't, because they never have problems with it.

Visibility attributes are necessary for controlling the public API, which D's private does just fine. From what I've seen, the only problem that we actually get from it is people being surprised that D's private is private to the module and then who want it to work differently (or who want something like private(this) in addition to private), because they think that it's good OO programming to have that and that it's dirty otherwise. But from a practical standpoint, I'm not sure that I've ever seen a bug reported that happened because private was for the module and not the type. If anything, the odds have been much higher that there's a compiler bug that has accidentally made something public or private when it shouldn't be (particularly since the change from private, public, etc. being protection attributes to visibility attributes seems to have been tricky enough that a number of subtle bugs were introduced in the process).

In my experience, there simply isn't anything to worry about here, and usually, the folks who are worried about it are worried about it because it's not what they're used to or because it doesn't fit in with their ideal view of OO programming. It is a topic that I've seen come up periodically over the nearly twenty years that I've been using D, so it's far from new, and it will probably never go away as long as things stay the way that they are, but it's almost always newcomers complaining because it's different and not people who have actually had problems who complain. And in general, folks who program with D the way that it is find that private works just fine as-is. Other aspects of the language may cause problems, but private being private to the module really hasn't been causing problems in general.

So, yes, I bring up moving code to other modules as a solution if you actually want to guarantee that it doesn't have access to the code in the module that it was in, because that will give you that guarantee. But it's not something that I'm arguing that folks should be doing as best practice, because it simply isn't necessary in practice.

And because I have not seen anything that private(this) is trying to solve as something that's actually causing problems, I don't see any reason to add a language feature to try to solve those problems.

- Jonathan M Davis



April 27
On 27/4/24 9:39, Jonathan M Davis wrote:
> What I'm saying is that there isn't actually a problem that needs solving.
> It's a theoretical issue and not one in reality. Programmers routinely
> program in D without running into any issues with private. Some of them even
> think that private is private to the class or struct and are surprised years
> later to find out that it isn't, because they never have problems with it.

Well, I'm one of these programmers that was bitten by this issue. Coming from Java, though, the idea of keeping a class per file / module didn't seem so alien, so I just went with it.

That being said, I would have very much preferred to have this feature available.

Another aspect not being discussed here are synchronized classes [1]. Note that:

> Member fields of a synchronized class cannot be public

This breaks apart in very subtle and hard to detect ways when you allow other code in the same module to access private members, even if it looks innocuous. Concurrency bugs are notably hard to detect and even harder to debug, and introducing backdoors like this doesn't exactly help.

And "just don't do it" seems quite a bit like "you're holding it wrong". That's just enforcing by convention, and at that point why not go with the python "private" way? Just tell people that they shouldn't do it, and done.

Note how this is totally opposite as how `const` is treated: no more "logical const", but hard const.

Now, you can argue that synchronized classes are a mistake, shouldn't exist in the first place, are misleading, whatever... but I also disagree here: they have their usage for simple use cases with POD-like objects that _need_ to be a class (for whatever the reason).

And the fact is that they exist, so unless and until you deprecate them, why not try to make them at least somewhat useful? FWIW, in my view they should also implicitly cast to / from shared as needed, just like immutable. Anyway, I digress.

So this is another real benefit of having private(this). If the only drawback is the hypothetical "complexity" that it could add (which, while not being a compiler expert, I very much doubt it would be too big), why not check the already existing implementation and see what corner cases really appear?

I mean, the feature is literally already implemented, can be tested, and some people must be already using it without big issues. Why not at least put it behind a preview switch?

[1]: https://dlang.org/spec/class.html#synchronized-classes