June 23, 2022
On Thursday, 23 June 2022 at 00:20:20 UTC, forkit wrote:
> private(this) enables data hiding *within* a module.
>
> you now have the tool needed to define an abstract data type *within* a module.
[...]
> I fail to see the downside (providing the syntax fits in well with the rest of the language, and doesn't stand out like a sore thumb).
>
> once it's in, people will use it, or not, and all this fuss will be long forgotten....

The downside, as with every new language feature, is the cost of implementation and maintenance, plus the opportunity cost of adding new features instead of working on polishing existing ones.

I freely acknowledge that private(this) has non-zero upside. It makes the language more expressive. That, by itself, is not enough to justify the addition of a new language feature.

In order for private(this) to be added to D, somebody must convince Walter and Atila that the expected upside of it is large enough to outweigh the downside inherent to adding *any* new language feature.

This is why people are asking for concrete examples, and are not satisfied with theoretical justifications alone. They don't just want to hear that private(this) *could* be useful; they want to be convinced that it *will* be useful, in practice.
June 23, 2022
On Thursday, 23 June 2022 at 00:48:13 UTC, Paul Backus wrote:
>
> ..

I disgree. It will mean D can finally be taken seriously.

With private(this), you are not longer constrained to defining a type using the D module mechanism.

You can now actually create 'real' user-defined types.

As we already now, in D, a type created using the modular mechanism, gets even less suppport than an int type.

By that I mean, I use can use an int type throughout my module, and it's invariants will hold. The compiler will make sure of this.

The same is not true for user-defined types, because they are enabled using the D module mechansim.

That is, they do not own their type, so they cannot declare let alone enforce invariants, since module owns them.

Thus any invariants of the user type cannot only be declared and enforced by the module, and not the type.

Such types are not user-defined types.

With private(this), D turns into a language that supports user-defined types.

That's a big deal, IMO.
June 23, 2022
On Thursday, 23 June 2022 at 00:31:52 UTC, forkit wrote:

> the argument that you have access to the module, and accidents won't happen, or 'just don't do it', is nonsensical.
>

I never said accidents won't happen. In fact, I'm sure I explicitly called this out as a real potential problem I see with private-to-the-module (especially when multiple people work on it). But I also noted that the same problem exists *within* the class because you can access any private member from any class method. There is absolutely no practical difference here.

One of the Effective Java books (IIRC, might have been a different book) pointed this out. If multiple methods modify a private member, then you can't maintain an invariant on that member. Java doesn't have built-in support for invariants like D. The recommended solution? Always modify members through a setter. But according to you, that's a problem because accidents can happen.

In D, the `invariant` feature allows you to maintain the invariants of any class from within the class. A class in its a module by itself is fully 100% encapsulated and the invariants are guaranteed. So if you have invariants you don't want violated, then put that class in its own module.
June 23, 2022
On Thursday, 23 June 2022 at 00:48:13 UTC, Paul Backus wrote:
>
> This is why people are asking for concrete examples, and are not satisfied with theoretical justifications alone. They don't just want to hear that private(this) *could* be useful; they want to be convinced that it *will* be useful, in practice.

I have of course cited numerous papers on this, should anyone 'actually' be interested in learning about it.

Anyone that uses abstract data types, should know, already, of the advantage of keeping that types representation 'private'.

I should not need to provide evidence for this. Do you know how silly that sounds?

'private(this)' in D, will finally provide the ability to do just that.

Of course, if you genuiely believe, that every abstact data type should go in it own module, then the is zero benefit from private(this).

Also, if you genuiely believe that programmers do not make mistakes, then there is zero benefit from private(this).

btw. The idea of private(this) does not take priority over more pressing issues in D.

That is a red-herring argument.

If it gets in, it's because someone chose to 'volunteer' their own time, to put it in.


June 23, 2022
On Thursday, 23 June 2022 at 00:58:34 UTC, Mike Parker wrote:
> On Thursday, 23 June 2022 at 00:31:52 UTC, forkit wrote:
>
>> the argument that you have access to the module, and accidents won't happen, or 'just don't do it', is nonsensical.
>>
>
> I never said accidents won't happen. In fact, I'm sure I explicitly called this out as a real potential problem I see with private-to-the-module (especially when multiple people work on it). But I also noted that the same problem exists *within* the class because you can access any private member from any class method. There is absolutely no practical difference here.

It's just a matter of scale.

Big long classes, and you're back in the same problem area.

But with private(this), you can put small, closely related, user-defined types, in the same module, and never have to worry about mistakes. Not ever. Not by you, and not by anyone else.


> One of the Effective Java books (IIRC, might have been a different book) pointed this out. If multiple methods modify a private member, then you can't maintain an invariant on that member. Java doesn't have built-in support for invariants like D. The recommended solution? Always modify members through a setter. But according to you, that's a problem because accidents can happen.
>

It's a matter of scale.

The smaller the scope, the easier it is to identify 'mistakes'.

I'd rather find that mistake in the definition of the class, then on line 1050 down the module, where some 'free' function is doing something I didn't anticipate (i.e. a mistake).

>
> In D, the `invariant` feature allows you to maintain the invariants of any class from within the class. A class in its a module by itself is fully 100% encapsulated and the invariants are guaranteed. So if you have invariants you don't want violated, then put that class in its own module.

Walter is already on the record as saying this is an unreasonable constraint to impose. Has he changed his view?

June 23, 2022
On Thursday, 23 June 2022 at 01:13:22 UTC, forkit wrote:
>
> ...
> In D, the `invariant` feature allows you to maintain the invariants of any class from within the class.
> ..

On re-reading that part again, I'm confused as to what it means.

What is this 'invariant' feature you mention?
June 23, 2022
On Thursday, 23 June 2022 at 01:19:11 UTC, forkit wrote:
> On Thursday, 23 June 2022 at 01:13:22 UTC, forkit wrote:
>>
>> ...
>> In D, the `invariant` feature allows you to maintain the invariants of any class from within the class.
>> ..
>
> On re-reading that part again, I'm confused as to what it means.
>
> What is this 'invariant' feature you mention?

https://dlang.org/spec/class.html#invariants

A class in its own module can fully maintain its invariants, even when multiple methods modify a variable. Every function call is bookended with the invariant check.

There's a hole in module scope in that we treat modules as part of the aggregate's private API, but not as part of the public API. A new level of protection wouldn't fully plug that hole, but running invariant checks on access to private member variables from within the module would.

This whole discussion has led me to the opinion that if a module is part of the private API, then we ought to be treating it as part of the public API in terms of invariant and synchronized.
June 23, 2022
On Thursday, 23 June 2022 at 01:27:23 UTC, Mike Parker wrote:
> On Thursday, 23 June 2022 at 01:19:11 UTC, forkit wrote:
>> On Thursday, 23 June 2022 at 01:13:22 UTC, forkit wrote:
>>>
>>> ...
>>> In D, the `invariant` feature allows you to maintain the invariants of any class from within the class.
>>> ..
>>
>> On re-reading that part again, I'm confused as to what it means.
>>
>> What is this 'invariant' feature you mention?
>
> https://dlang.org/spec/class.html#invariants
>
> A class in its own module can fully maintain its invariants, even when multiple methods modify a variable. Every function call is bookended with the invariant check.
>

So obviously, the problem here is that the scoping for invariant check is wrong. But you already started a thread about this, so I assume you understand this fairly well.

June 23, 2022
On Thursday, 23 June 2022 at 01:27:23 UTC, Mike Parker wrote:
> A class in its own module can fully maintain its invariants, even when multiple methods modify a variable. Every function call is bookended with the invariant check.
>
> There's a hole in module scope in that we treat modules as part of the aggregate's private API, but not as part of the public API. A new level of protection wouldn't fully plug that hole, but running invariant checks on access to private member variables from within the module would.
>
> This whole discussion has led me to the opinion that if a module is part of the private API, then we ought to be treating it as part of the public API in terms of invariant and synchronized.

Your proposal would lead to the following situation:

    struct EvenNumber
    {
        private int n;
        invariant (n & 1 == 0);

        this(int n) { this.n = n; }

        void addTwoMethod() { this.n++; this.n++; } // ok
    }

    void addTwoUFCS(ref EvenNumber this_)
    {
        this_.n++; this_.n++; // error - violates invariant
    }

IMO the correct way to view this is that code in the same module as a class is part of the class's implementation, just as much as code in the actual class body. (Or if you prefer, they are both part of the module's implementation, since the module is the fundamental unit of decomposition here, not the class.)

Note that sometimes, one is actually required to implement methods as UFCS functions rather than actual aggregate members--for example, to work around issue 5710 [1], or if one wants to write a struct method that takes its "this" argument by value instead of by reference.

[1] https://issues.dlang.org/show_bug.cgi?id=5710
June 23, 2022
On Thursday, 23 June 2022 at 01:45:22 UTC, Paul Backus wrote:

> Your proposal would lead to the following situation:
>
>     struct EvenNumber
>     {
>         private int n;
>         invariant (n & 1 == 0);
>
>         this(int n) { this.n = n; }
>
>         void addTwoMethod() { this.n++; this.n++; } // ok
>     }
>
>     void addTwoUFCS(ref EvenNumber this_)
>     {
>         this_.n++; this_.n++; // error - violates invariant
>     }

I could live with that particular inconsistency. The bigger downside I see is that you'd potentially have multiple invariant checks for the same class on a single function call. And whether that's acceptable depends on how common this is in practice.

>
> IMO the correct way to view this is that code in the same module as a class is part of the class's implementation, just as much as code in the actual class body. (Or if you prefer, they are both part of the module's implementation, since the module is the fundamental unit of decomposition here, not the class.)
>

As long as the invariant checks only run on those functions that actually touch a private member. There would potentially be checks for multiple classes running before and after each function call, so doing it on every function call in the module is overkill.

And that would also have to include any public member functions of any classes that access any private members of other classes in the same module. It seems more complex to implement than just doing a rewrite to getters/setters.

An even simpler option would be to disallow access to any private members in a module on classes that have invariants. Not something I'd advocate, but a possibility.