Thread overview | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
August 25, 2013 Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
As they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia. The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant. For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls. Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check. invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion). Any other pattern it doomed to create infinite recursion for non trivial invariant checks. |
August 25, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | deadalnix:
> As they are defined now, invariant are plain useless.
Don't forget to add this issue to Bugzilla. So we will keep memory of it.
Bye,
bearophile
|
August 25, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | On Sunday, 25 August 2013 at 13:44:49 UTC, bearophile wrote: > deadalnix: >> As they are defined now, invariant are plain useless. > > Don't forget to add this issue to Bugzilla. So we will keep memory of it. > > Bye, > bearophile http://d.puremagic.com/issues/show_bug.cgi?id=10889 Done but I'm not sure this is a bug. This work as designed, it is simply that the design is improvable :D |
August 25, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On 8/25/13 5:16 AM, deadalnix wrote: > As they are defined now, invariant are plain useless. I find myself > disabling them one by one in my code as soon as cases get outside simple > trivia. > > The problem is that invariant are checked at the beginning/end on public > function calls. As a consequence, it is impossible to use any public > method in an invariant. But this is what they are for - check the state of an object upon access through the client-accessible interface. Simple code factorization (use a private method forwarded to by the public one) takes care of this situation. > For instance, just right now I did refactor a struct to use bitfields, > in order to make it more compact. Now I have to remove the invariant as > field access are now function calls. I don't get this case. Code? > Another typical situation is when you want to assert certain properties > in a class hierarchy, where calling virtual method is part of the > invariant check. > > invariant should (and must to be useful) be inserted at callee point, > when the callee isn't itself subject to invariant insertion, and around > public memeber manipulation (when the manipulator isn't subject to > invariant insertion). > > Any other pattern it doomed to create infinite recursion for non trivial > invariant checks. There might be something here, but more like a performance problem. I do see how calls of public methods from an invariant could cause trouble, but I'd say that's a problem with the invariant design. Andrei |
August 25, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sunday, 25 August 2013 at 16:22:01 UTC, Andrei Alexandrescu wrote: > On 8/25/13 5:16 AM, deadalnix wrote: >> As they are defined now, invariant are plain useless. I find myself >> disabling them one by one in my code as soon as cases get outside simple >> trivia. >> >> The problem is that invariant are checked at the beginning/end on public >> function calls. As a consequence, it is impossible to use any public >> method in an invariant. > > But this is what they are for - check the state of an object upon access through the client-accessible interface. Simple code factorization (use a private method forwarded to by the public one) takes care of this situation. > Yes that can be worked around, but the code ends up more and more complex, with many function forwarding to one another. This can become very messy with class hierarchies. >> For instance, just right now I did refactor a struct to use bitfields, >> in order to make it more compact. Now I have to remove the invariant as >> field access are now function calls. > > I don't get this case. Code? > struct Identifiable { union { Symbol sym; Expression expr; Type type; } import d.ast.base : TypeQualifier; import std.bitmanip; mixin(bitfields!( Tag, "tag", 2, TypeQualifier, "qual", 3, uint, "", 3, )); @disable this(); // For type inference. this(typeof(null)); this(Identifiable i) { this = i; } this(Symbol s) { tag = Tag.Symbol; sym = s; } this(Expression e) { tag = Tag.Expression; expr = e; } this(QualType qt) { tag = Tag.Type; qual = qt.qualifier; type = qt.type; } } I used to have an invariant that check the sanity of the tagged union, but now I have to double the interface to the bitfield. This one is still fixable by doubling the interface, but the same thing with the bitfield in a super class from another module and you are doomed. >> Another typical situation is when you want to assert certain properties >> in a class hierarchy, where calling virtual method is part of the >> invariant check. >> >> invariant should (and must to be useful) be inserted at callee point, >> when the callee isn't itself subject to invariant insertion, and around >> public memeber manipulation (when the manipulator isn't subject to >> invariant insertion). >> >> Any other pattern it doomed to create infinite recursion for non trivial >> invariant checks. > > There might be something here, but more like a performance problem. I do see how calls of public methods from an invariant could cause trouble, but I'd say that's a problem with the invariant design. > > > Andrei This is a performance issue as well. But mostly it makes invariant useless in many cases when class hierarchies are in place and probably the worst part is that invariant are not even ensured as they do not trigger when writing to public fields (as triggered by the callee, and we have no callee in this case). |
August 25, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | deadalnix:
> Done but I'm not sure this is a bug. This work as designed, it is simply that the design is improvable :D
It's not a bug, it's an enhancement, because according to you it could be a small design bug.
Don't forged to add to the bug report good comments coming from Andrei or other people from this thread.
Bye,
bearophile
|
August 25, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Sunday, August 25, 2013 14:16:28 deadalnix wrote:
> As they are defined now, invariant are plain useless. I find myself disabling them one by one in my code as soon as cases get outside simple trivia.
>
> The problem is that invariant are checked at the beginning/end on public function calls. As a consequence, it is impossible to use any public method in an invariant.
>
> For instance, just right now I did refactor a struct to use bitfields, in order to make it more compact. Now I have to remove the invariant as field access are now function calls.
>
> Another typical situation is when you want to assert certain properties in a class hierarchy, where calling virtual method is part of the invariant check.
>
> invariant should (and must to be useful) be inserted at callee point, when the callee isn't itself subject to invariant insertion, and around public memeber manipulation (when the manipulator isn't subject to invariant insertion).
>
> Any other pattern it doomed to create infinite recursion for non trivial invariant checks.
What drives me nuts is the fact that it gets checked when opAssign is called (rather than just when exiting it). That makes it impossible to have a member in an invalid state (either through an init which has an invalid state similar to how floating point values due or because the object hasn't been initialized yet) and have an invariant. At present, emplace will blow up in your face if you try and have an invariant with such types. The result is that I almost never use invariant. It ends up working better to just create a private function which is the invariant and asserting explicitly in every function where you want the check, but most of the time, that's enough of a pain that the result is that it's just not checked.
Unfortunately, my enhancement request on that (not having the invariant called on entry to opAssign) was shot down, because there are some opAssigns which require that the previous state be valid rather than just replacing it (which means that what you really need is a way to declare an invariant but disable it in certain places).
- Jonathan M Davis
|
August 25, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On 8/25/13 2:56 PM, Jonathan M Davis wrote:
> On Sunday, August 25, 2013 14:16:28 deadalnix wrote:
>> As they are defined now, invariant are plain useless. I find
>> myself disabling them one by one in my code as soon as cases get
>> outside simple trivia.
>>
>> The problem is that invariant are checked at the beginning/end on
>> public function calls. As a consequence, it is impossible to use
>> any public method in an invariant.
>>
>> For instance, just right now I did refactor a struct to use
>> bitfields, in order to make it more compact. Now I have to remove
>> the invariant as field access are now function calls.
>>
>> Another typical situation is when you want to assert certain
>> properties in a class hierarchy, where calling virtual method is
>> part of the invariant check.
>>
>> invariant should (and must to be useful) be inserted at callee
>> point, when the callee isn't itself subject to invariant
>> insertion, and around public memeber manipulation (when the
>> manipulator isn't subject to invariant insertion).
>>
>> Any other pattern it doomed to create infinite recursion for non
>> trivial invariant checks.
>
> What drives me nuts is the fact that it gets checked when opAssign is called
> (rather than just when exiting it). That makes it impossible to have a member
> in an invalid state (either through an init which has an invalid state similar
> to how floating point values due or because the object hasn't been initialized
> yet) and have an invariant. At present, emplace will blow up in your face if
> you try and have an invariant with such types.
emplace() should not call assignment for elaborate types.
Andrei
|
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On 08/25/2013 05:16 AM, deadalnix wrote: > The problem is that invariant are checked at the beginning/end on public > function calls. As a consequence, it is impossible to use any public > method in an invariant. That's a very interesting observation. Could the solution be running the invariant only once, at the outermost public function call? Hm... It would have to be a runtime feature then, right? Every public function would have calls to the invariant but those calls would have to be elided at runtime. I think... Here is another interesting observation: It is acceptable and quite normal that the object is in limbo state during a public member function. As a consequence, any function that operates on the object must use the object in a write-only fashion during that time frame. This is true even for non-member functions that the object is passed to. So, in theory, even a logging function cannot use the object. Hm... I jokingly propose a new function parameter attribute: 'static out', meaning write-only. ('out' already has a conflicting meaning and 'static' is not overloaded enough yet. :p) Ali |
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Monday, 26 August 2013 at 06:14:02 UTC, Ali Çehreli wrote: > On 08/25/2013 05:16 AM, deadalnix wrote: > > > The problem is that invariant are checked at the > beginning/end on public > > function calls. As a consequence, it is impossible to use any > public > > method in an invariant. > > That's a very interesting observation. Could the solution be running the invariant only once, at the outermost public function call? Hm... It would have to be a runtime feature then, right? Every public function would have calls to the invariant but those calls would have to be elided at runtime. I think... > No need for runtime check :D We simply need to add invariant check in the caller, not the callee, and not introduce them if the caller is itself subject to invariant insertion when called. > Here is another interesting observation: It is acceptable and quite normal that the object is in limbo state during a public member function. As a consequence, any function that operates on the object must use the object in a write-only fashion during that time frame. This is true even for non-member functions that the object is passed to. So, in theory, even a logging function cannot use the object. Hm... > The question of constness of invariant/contracts has been raised, I do think the object should be const, but Andrei think otherwise. |
Copyright © 1999-2021 by the D Language Foundation