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...
>
> 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...
If you will indulge a D newbie, lurker and former Eiffelist:
In design-by-contract theory, the invariant is part of the definition of a user-defined type. If type T has an invariant, an object t of that type is not a "real T" if it does not meet the invariant while publicly accessible (that is, while it is not in the hands of a member function of class T).
For instance, the purpose of the constructor is to establish the invariant -- some Eiffelists argue that this is the *sole* purpose of the constructor, and that constructors which perform initialization beyond that are overstepping their bounds.
For another point, T t should be capable of being tested for its invariant (and passing that test) anywhere and at any time it is publicly accessible. It is only a matter of convenience that implementations test the invariant at entry to and exit from public member functions of class T. That convenience relies on the guarantee that *only* T member functions can be allowed to modify the state of object t -- all other operations in the program must treat t as const (and can, of course, *rely* on t being const)... which, in turn, has all kinds of implications about what you can safely make public about a T object.
All of this has implications for D's contract guarantees. For instance, you may not design an invariant that fails on T.init (which is the state of t after you call t.clear() ). In other words, D's object model doesn't necessarily match strict DbC theory. And Jonathon Davis's notion of a t that "isn't really a T" but should be acceptable by T.opAssign() also falls outside the theory.
Offhand, I can think of two approaches that might address these desires without too badly weakening the invariant's guarantee:
+ attach an internal "depth gauge" to each T t. At entry to any public T member function, the depth gauge is incremented; at exit, decremented. Invariant is tested *only* when the depth gauge transitions from or to zero: that is, at its transition from "surfaced" (public) to "submerged" (in the hands of T class functions) or vice-versa. This would allow the implementation to elide any invariant tests while T member functions call other functions, even functions that are themselves public T member functions.
* an attribute which tags a public T function as accepting "broken Ts". Such a function would be expected to establish the invariant in the object, and test that invariant, at exit, but would *not* test the invariant at entry.
These two overlap but not entirely, and I'm still thinking about whether you would want both, or just one or the other. As I say, this is off the top of my head.
I hope I have contributed a useful notion, and not just muddied the waters. Thanks for listening.
-- Dai
|
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sunday, 25 August 2013 at 23:02:29 UTC, Andrei Alexandrescu wrote: > 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 A pull for that is ready, and need review... Just by coincidence I've hit this problem with emplace and invariant and assignment just yesterday, and talked with monarch dodra about the very same issue. https://github.com/D-Programming-Language/phobos/pull/1082 - Paolo Invernizzi |
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Davidson Corry | On Monday, 26 August 2013 at 07:20:56 UTC, Davidson Corry wrote:
> ...
By the way it makes me think that invariant should run via CTFE on T.init for all types that have invariant contracts. The fact that default-initialize value may have a broken contract greatly diminishes value of the whole system.
|
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On 08/26/2013 08:49 AM, deadalnix wrote:
>
> 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.
The issue with this is that the invariant is not part of the public interface.
|
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote: > On 08/26/2013 08:49 AM, deadalnix wrote: > > > >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. > > The issue with this is that the invariant is not part of the public interface. The fact that contracts are not part of the public interface greatly limits the usefulness of DbC in D. If they *were* part of the public interface, there'd be more options to improve it. But this isn't the first time this is brought up, and it still seems no solution is in sight. :-( T -- Nothing in the world is more distasteful to a man than to take the path that leads to himself. -- Herman Hesse |
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dicebot | On Monday, 26 August 2013 at 14:16:04 UTC, Dicebot wrote:
> On Monday, 26 August 2013 at 07:20:56 UTC, Davidson Corry wrote:
>> ...
>
> By the way it makes me think that invariant should run via CTFE on T.init for all types that have invariant contracts. The fact that default-initialize value may have a broken contract greatly diminishes value of the whole system.
Not all invariants can run at compile time.
|
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | 26-Aug-2013 20:47, H. S. Teoh пишет: > On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote: >> On 08/26/2013 08:49 AM, deadalnix wrote: >>> >>> 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. >> >> The issue with this is that the invariant is not part of the public >> interface. > > The fact that contracts are not part of the public interface greatly > limits the usefulness of DbC in D. If they *were* part of the public > interface, there'd be more options to improve it. > > But this isn't the first time this is brought up, and it still seems no > solution is in sight. :-( > s/solution/implementation/ > > T > -- Dmitry Olshansky |
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On 08/26/2013 06:47 PM, H. S. Teoh wrote:
> On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote:
>> On 08/26/2013 08:49 AM, deadalnix wrote:
>>>
>>> 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.
>>
>> The issue with this is that the invariant is not part of the public
>> interface.
>
> The fact that contracts are not part of the public interface greatly
> limits the usefulness of DbC in D. If they *were* part of the public
> interface, there'd be more options to improve it.
>
> But this isn't the first time this is brought up, and it still seems no
> solution is in sight. :-(
>
>
> T
>
(This was not a statement about Ds implementation. The invariant is by design a place to state facts about implementation details.)
|
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Mon, Aug 26, 2013 at 07:20:21PM +0200, Timon Gehr wrote: > On 08/26/2013 06:47 PM, H. S. Teoh wrote: > >On Mon, Aug 26, 2013 at 06:16:25PM +0200, Timon Gehr wrote: > >>On 08/26/2013 08:49 AM, deadalnix wrote: > >>> > >>>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. > >> > >>The issue with this is that the invariant is not part of the public interface. > > > >The fact that contracts are not part of the public interface greatly limits the usefulness of DbC in D. If they *were* part of the public interface, there'd be more options to improve it. > > > >But this isn't the first time this is brought up, and it still seems no solution is in sight. :-( > > > > > >T > > > > (This was not a statement about Ds implementation. The invariant is by design a place to state facts about implementation details.) Oh. I misunderstood what you said, then. T -- The two rules of success: 1. Don't tell everything you know. -- YHL |
August 26, 2013 Re: Invariants are useless the way they are defined | ||||
---|---|---|---|---|
| ||||
Posted in reply to Peter Alexander | On Monday, 26 August 2013 at 16:49:01 UTC, Peter Alexander wrote:
> On Monday, 26 August 2013 at 14:16:04 UTC, Dicebot wrote:
>> On Monday, 26 August 2013 at 07:20:56 UTC, Davidson Corry wrote:
>>> ...
>>
>> By the way it makes me think that invariant should run via CTFE on T.init for all types that have invariant contracts. The fact that default-initialize value may have a broken contract greatly diminishes value of the whole system.
>
> Not all invariants can run at compile time.
True, but it will at least make a notable cluster of potential issues into compile-time errors. Actually, I'd expect non-CTFE invariant to be quite rare - those rarely do any I/O or some weird pointer forging.
If invariant is not CTFE-able it should be deferred to runtime to the initial point of the aggregate construction.
|
Copyright © 1999-2021 by the D Language Foundation