| |
| Posted by Jonathan M Davis in reply to Max Samukha | PermalinkReply |
|
Jonathan M Davis
Posted in reply to Max Samukha
| On Thursday, January 25, 2024 8:03:41 AM MST Max Samukha via Digitalmars-d- announce wrote:
> On Monday, 22 January 2024 at 23:28:40 UTC, Jonathan M Davis
>
> wrote:
> > Of course, ultimately, different programmers have different preferences, and none of us are going to be happy about everything in any language.
>
> It's not only about preferences. The feature is inconsistent with how 'invariant' and 'synchronized' are specified. They imply class-instance-level private, while the language dictates module-level. Consider:
>
> ```
> synchronized class C
> {
> private int x;
> private int y;
>
> invariant () { assert (x == y); }
> }
>
> void foo(C c)
> {
> // mutate c
> }
> ```
>
> With module-level private, 'foo' is part of C's public interface, but it neither locks on c, nor runs the invariant checks. I personally have no idea how to fix that sensibly except by ditching class invariant/synchronized entirely.
Well, sychronized is actually a function attribute, not a class attribute (TDPL talks about synchronized classes, but they've never actually been a thing; it was just a planned idea that was never implemented). You can stick synchronized on the class itself, but it still only affects the member functions. So, mutating the class object via non-member functions in the module really isn't any different from mutating the object with member functions which aren't marked with synchronized. So, if anything here, I would argue that the confusion comes from being allowed to stick attributes on a class and then have them affect the member functions. It does allow you to stick the attribute in one place and then affect the entire class, but I'm inclined to think that it probably shouldn't have been allowed in cases where the attribute isn't actually for the class itself.
Of course, the change that I'd really like to see here is synchronized removed from the language, since I think that it was definitely a misfeature (along with having a monitor inside of every class instance to allow synchronized to work, whether the type is shared or not or has an synchronized methods or not).
Regardless, because synchronized is not at all a class attribute, I don't agree that it implies anything related to class-level private, much as I can see how being allowed to put it directly on a class could cause confusion.
As for invariants, all that the spec promises is that they're called when public member functions are called. So, again, having a module-level function directly mutate the members doesn't really violate anything. However, the part there that I do agree is questionable is that because the module-level function could be public, it makes it so that it's pretty easy to end up in a situation where an invariant is skipped when the object is mutated by calling public functions from the module. But there are also likely to be cases where it's useful to be able to bypass the invariant like that (though obviously, it then requires that the code maintain the invariant, just like @trusted code needs to maintain the promises of @safe). So, I don't think that it's necessarily a problem that the language works this way, but it's certainly true that it's something to be mindful of. And if you want to explictly run the invariant in such sitations, then you can just assert the class reference. But as with anything related to private, if you want to guarantee that something only accesses an object via its public API, you can always just put it in another module.
- Jonathan M Davis
|