Thread overview
Compile time range checking/asserts
Sep 15, 2010
Borden Rhodes
Sep 15, 2010
bearophile
Sep 15, 2010
bearophile
September 15, 2010
Good morning, list,

I know that D has support for ranges in for-each statements and in array bounds checking, but I'm curious if it also has a facility for compile-time range checking or assertions on individual variables.

For example, using the Java Tutorial's venerable Bicycle class, say I had a property <code>int gear;</code> which indicated the bicycle's current gear.  Let's say all bicycles of this class have 6 gears.  Typically, I would write a changeGear method as:

<code>
void changeGear(int newGear) {
        if ( 1 <= newGear && newGear <= 6)
                gear = newGear;
        else
                throw SomeKindOfException();
}
</code>

which would include the if statement to make sure that newGear would be passed a number in the correct range and a throw statement to deal with cheaters.

Does D allow - or would there be logic in creating - an ability to define the acceptable ranges when the variable is initialised?  So, say I wanted gear only to accept values between 1 and 6 inclusive.  Could I declare something to the effect of:

<code>int gear {1...6} = 1;</code>

Which could read, "create a new int, called gear, which only accepts values between 1 and 6, inclusive, and initialise it to 1."  Then, if I, or some coder who forgot how many gears my Bicycle has, tried to call:

</code>bicycle.changeGear( 12 );</code>

the compiler would catch that <code>gear = newGear;</code> is assigning a value out of range and throw a compile-time error.  Can D do this?  Should it do this?

With thanks,

Borden
September 15, 2010
Borden Rhodes:
> <code>int gear {1...6} = 1;</code>

I think Walter said that Ranged Types are a "failed feature" of Pascal/Ada lineage, but I don't agree and I think they are useful to increase code safety, a possible syntax from yours:

typedef int { 1 ... 6 } Gear;

Using a struct with "alias this" and a struct invariant that enforces the range, plus properties, is possible, but such struct:
- Doesn't act transparently as a int subclass;
- Syntax-wise it's longer to define;
- The invariant is not enforced in all situations.

struct Gear {
    int g_ = 1;
    this(int g) { this.g_ = g; }
    invariant() { assert(this.g_ >= 1 && this.g_ <= 6); }
    @property void g(int x) { this.g_ = x; }
    @property int g() { return this.g_; }
    alias g this;
}

Bye,
bearophile
September 15, 2010
> - Doesn't act transparently as a int subclass;
> - Syntax-wise it's longer to define;
> - The invariant is not enforced in all situations.

And the compiler in most situations doesn't "run" the invariant at compile time (unless you define an enum).

Well, a test shows that it doesn't work (dmd 2.048) even if you use enum, I don't know why:

struct Foo {
    int x;
    this(int y) { this.x = y; }
}
void main() {
    enum Foo f = Foo(1);
}

Bye,
bearophile