www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The relationship of invariants and alias this

reply "Mark Isaacson" <turck11 hotmail.com> writes:
I've encountered something which is probably intentional 
behavior, but that I think is interesting and may merit 
discussion. When you do "alias this" and have an invariant, any 
methods that are forwarded to the aliased member do not invoke 
your invariant methods.

This prevents me from writing a really sleek 10-liner to the tune 
of:

struct ValueRestrictedInteger(int lowerBound, int upperBound) {
   int value;
   alias value this;

   this (int rhs) { value = rhs; }

   invariant() {
     assert (value >= lowerBound && value <= upperBound);
   }

   void forDemonstrationOnly() {}
}

unittest {
   ValueRestrictedInteger!(0, 100) x = 0;
   x += 10;
   x -= 100; //This works, but I don't think it should
   x.forDemonstrationOnly(); //This causes the assertion to fire

   ValueRestrictedInteger!(0, 100) y = -100; //This also would hit 
the assertion
}

I realize that this is not the most efficient way to implement 
this concept and that the invariant gets compiled out in a 
-release build... I still think this is a pretty nifty piece of 
code whose semantics are not unreasonable. Why shouldn't an 
aliased method be subject to our invariants?
Nov 10 2014
parent reply "Mark Isaacson" <turck11 hotmail.com> writes:
Should be noted that this behavior is the same regardless of 
whether or not I do an "alias this" on a primitive type as shown 
above or on a user defined type.
Nov 10 2014
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Nov 11, 2014 at 05:03:00AM +0000, Mark Isaacson via Digitalmars-d wrote:
 Should be noted that this behavior is the same regardless of whether
 or not I do an "alias this" on a primitive type as shown above or on a
 user defined type.
Sounds like a bug. Please file an issue at http://issues.dlang.org/ T -- It's amazing how careful choice of punctuation can leave you hanging:
Nov 10 2014
parent reply "Mark Isaacson" <turck11 hotmail.com> writes:
On Tuesday, 11 November 2014 at 06:21:18 UTC, H. S. Teoh via 
Digitalmars-d wrote:
 On Tue, Nov 11, 2014 at 05:03:00AM +0000, Mark Isaacson via 
 Digitalmars-d wrote:
 Should be noted that this behavior is the same regardless of 
 whether
 or not I do an "alias this" on a primitive type as shown above 
 or on a
 user defined type.
Sounds like a bug. Please file an issue at http://issues.dlang.org/ T
Exciting/encouraging! Done. https://issues.dlang.org/show_bug.cgi?id=13710
Nov 10 2014
parent reply "angel" <andrey.gelman gmail.com> writes:
Wait !
"x.value -= 100;" would call the invariant ?
Alias this only rewrites your expression:
"x -= 100;" becomes "x.value -= 100;"
No method is called. Then there is no reason (is there ?) to call 
the invariant.
If you would create getter/setter properties ...
Nov 11 2014
next sibling parent "Mark Isaacson" <turck11 hotmail.com> writes:
On Tuesday, 11 November 2014 at 08:18:36 UTC, angel wrote:
 Wait !
 "x.value -= 100;" would call the invariant ?
 Alias this only rewrites your expression:
 "x -= 100;" becomes "x.value -= 100;"
 No method is called. Then there is no reason (is there ?) to 
 call the invariant.
 If you would create getter/setter properties ...
Intriguing - thanks :). Looks like you found the reason for the current implementation; what you've written is the "lowering" of alias this. From a semantic perspective though, we are claiming that "value *is* this"; there is an IsA relationship and I'd argue that the invariant should still apply because we are modifying "this", in a way. I totally see where you're coming from though; that's why I wanted to open the floor for discussion as to what the more desirable behavior is.
Nov 11 2014
prev sibling parent reply "ZombineDev" <valid_email he.re> writes:
AFAIU, even if you had a getter in the alias this:

---
import std.stdio;

struct ValueRestrictedInteger(int lowerBound, int upperBound) {
   int value;
   auto ref get() { return value; }	
   alias get this;

   this (int rhs) { value = rhs; }

   invariant() {
     assert (value >= lowerBound && value <= upperBound);
   }

   void forDemonstrationOnly() {}
}
---

It would still not work:

---
unittest {
   ValueRestrictedInteger!(0, 100) x = 0;

   x -= 100; //is probably lowered to something like this:
   // 1) invariant();
   // 2) int* __temp = &x.value; // this is what get() does
   // 3) invariant();
   // 4) *__temp = *temp - 100;

   //...
}
---

Obviously, 4) will not trigger the invariant because it doesn't 
call any public functions.
Nov 11 2014
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 11/11/14 6:48 AM, ZombineDev wrote:
 AFAIU, even if you had a getter in the alias this:

 ---
 import std.stdio;

 struct ValueRestrictedInteger(int lowerBound, int upperBound) {
    int value;
    auto ref get() { return value; }
    alias get this;

    this (int rhs) { value = rhs; }

    invariant() {
      assert (value >= lowerBound && value <= upperBound);
    }

    void forDemonstrationOnly() {}
 }
 ---

 It would still not work:

 ---
 unittest {
    ValueRestrictedInteger!(0, 100) x = 0;

    x -= 100; //is probably lowered to something like this:
    // 1) invariant();
    // 2) int* __temp = &x.value; // this is what get() does
    // 3) invariant();
    // 4) *__temp = *temp - 100;

    //...
 }
 ---

 Obviously, 4) will not trigger the invariant because it doesn't call any
 public functions.
Correct, the only real solution is to wrap with the actual operators and opDispatch. -Steve
Nov 11 2014