June 21, 2022

On Tuesday, 21 June 2022 at 01:20:27 UTC, Mike Parker wrote:

>

On Tuesday, 21 June 2022 at 01:17:14 UTC, Mike Parker wrote:

>

On Tuesday, 21 June 2022 at 01:01:09 UTC, deadalnix wrote:

>

I'm pretty sure x can be encapsulated in some struct here.

Yes, I did note that later in the post you just replied to :-) Put it in a struct and then a different module. So restricted is absolutely pointless because of that. It's the obvious solution that I overlooked until I read your comment about two classes bundled in one.

That said, spilling class internals out into a separate module is a bit "icky". Targeted invariants would be nice. Not a must have, probably a niche case, but definitely nice to have.

No, you don't spill the struct internal, in fact you don't need to spill anything specific to the struct. What you want to spill is the restricted write mechanism so if goes through a validation step.

This can all be done in a template and provided as this for the whole application to use any time someone wants a restricted field.

That being said, the invariant really doesn't trigger in a way that is consistent with the other rules of the language, so there is something there.

IMO the ivnariant has two problems (one of them I opened a bug repport for, but idk what happened to it):
1/ It need to trigger for anything that touches the class's privates parts, not just members.
2/ Code that trigger the invariant must not call it again down the road, as this tends to cause infinite recursions and/or trigger the invariant mid transform at a time the object might not be valid (and therefore fail the invariant check) even though at no point any external observer can see object in an invalid state.

June 21, 2022

On Tuesday, 21 June 2022 at 01:20:27 UTC, Mike Parker wrote:

>

Targeted invariants would be nice. Not a must have, probably a niche case, but definitely nice to have.

The intent is to give the compiler a constraint, so the assumption that the compiler doesn’t have access to the full constraint is the same as assuming that the programmer doesn’t know what he is doing. The right thing to do is to help the programmer and reject such illdpecified constraints.

If you specify preconditions/postconditions then the exit check should be considered part of the assumed precondition and the asserted postcondition. If you allow the class invariant to be hidden from the compiler then you mess up the preconditions.

Asserts only add information that is redundant and would be removed by a perfect static analysis. If that isn’t possible then either the invariant/assert is badly written or the program is wrong.

June 21, 2022

On Tuesday, 21 June 2022 at 07:26:50 UTC, Ola Fosheim Grøstad wrote:

>

If you specify preconditions/postconditions then the exit check should be considered part of the assumed precondition and the asserted postcondition.

The «exit check» that is the class invariant.

June 21, 2022

On Monday, 20 June 2022 at 11:04:16 UTC, Mike Parker wrote:

>
class E
{
   restricted int _y;
   restricted(_y) void y(int newY) { _y = newY; }
}

The "right" way would be to hide the variable in the function's scope. We can hide static variables in this way, but not instance ones.

class E
{
void(int newY) {
this int y; // 'this' as storage class will likely be grammatically ambiguous
newY = y;
}
}

However, this will only work because currently it is impossible to access variables from the outside at all. If it's changed, the fundamental problem - 'private' not being scope-level - will return.

I am actually against any changes to the current situation. Module-level private is designed to break encapsulation, so breaking the invariants by module-level functions is expected.

June 21, 2022

On Tuesday, 21 June 2022 at 08:37:55 UTC, Max Samukha wrote:

>

class E
{
void(int newY) {
this int y; // 'this' as storage class will likely be grammatically ambiguous
newY = y;
}
}

Typo. Should be:

class E
{
    void(int newY) {
        this int y;
        y = newY;
    }
}
June 21, 2022

On Tuesday, 21 June 2022 at 08:37:55 UTC, Max Samukha wrote:

>

I am actually against any changes to the current situation. Module-level private is designed to break encapsulation, so breaking the invariants by module-level functions is expected.

Yes, I agree. Either be consistent with the idea that the module is the boundary for encapsulation or not.

It is better to give priority to plugging the holes that prevent people from writing code than preventing the compiler from accepting ill-conceived programs.

The current D compiler accepts too many programs, but that can be fixed with a linter.

June 21, 2022

On Tuesday, 21 June 2022 at 08:37:55 UTC, Max Samukha wrote:

>

However, this will only work because currently it is impossible to access variables from the outside at all.

variables -> function-local variables

June 21, 2022

On Tuesday, 21 June 2022 at 08:40:28 UTC, Max Samukha wrote:

>

On Tuesday, 21 June 2022 at 08:37:55 UTC, Max Samukha wrote:

>

class E
{
void(int newY) {
this int y; // 'this' as storage class will likely be grammatically ambiguous
newY = y;
}
}

Typo. Should be:

class E
{
    void(int newY) {
        this int y;
        y = newY;
    }
}

this won't solve the 'problem' if the variable has to be accessed by multiple functions.
Anyway, i don't think this feature is needed at all. if you run into those problems, you are likely packing too many things into a single class, and you likely should move them to a seperate struct in a seperate module.

June 21, 2022
On Tuesday, 21 June 2022 at 01:18:38 UTC, forkit wrote:
> On Monday, 20 June 2022 at 11:04:16 UTC, Mike Parker wrote:
>> [...]
>
> I don't want to rehash it either, so I'll limit this to one post in this thread.
>
> [...]

you can already express the intent more clearly by putting the class in either a seperate module, or function scope.
we _do_ understand your point, we disagree on it's usefulness and the complexity it adds to the language.
June 21, 2022

On Tuesday, 21 June 2022 at 02:05:22 UTC, deadalnix wrote:

>

On Tuesday, 21 June 2022 at 01:20:27 UTC, Mike Parker wrote:

>

On Tuesday, 21 June 2022 at 01:17:14 UTC, Mike Parker wrote:

>

[...]

That said, spilling class internals out into a separate module is a bit "icky". Targeted invariants would be nice. Not a must have, probably a niche case, but definitely nice to have.

No, you don't spill the struct internal, in fact you don't need to spill anything specific to the struct. What you want to spill is the restricted write mechanism so if goes through a validation step.

This can all be done in a template and provided as this for the whole application to use any time someone wants a restricted field.

That being said, the invariant really doesn't trigger in a way that is consistent with the other rules of the language, so there is something there.

IMO the ivnariant has two problems (one of them I opened a bug repport for, but idk what happened to it):
1/ It need to trigger for anything that touches the class's privates parts, not just members.
2/ Code that trigger the invariant must not call it again down the road, as this tends to cause infinite recursions and/or trigger the invariant mid transform at a time the object might not be valid (and therefore fail the invariant check) even though at no point any external observer can see object in an invalid state.

+1
i absolutely agree with this.
if anything, i'd like to see more control over when invariants are actually checked.

something like

invariant(default)
    {
        assert(1 <= day && day <= 31);
        assert(0 <= hour && hour < 24);
    }
invariant(always) {
        assert(1 <= sec && sec <= 60);
}