Troubles with user defined subtypes

bearophile bearophileHUGS at lycos.com
Mon Dec 17 14:34:51 PST 2012


Regarding the recent discussions on SafeInts and HalfFloats, I 
find some difficulties in using user-defined sub-range of a type. 
Let's say I want to define a new type as subset of all the chars, 
that contains only the digits.

This is a very common need in my programs, I often want integral 
values that are allowed to be only a subrange (or subset, if they 
are sparse) of a built-in integral type.

(In Ada language there is syntax and semantics in the type system 
to make this easy, safe and nice).

Here I have shown some general problems associated with the 
creation and use of user-defined subtypes:

http://forum.dlang.org/thread/jhkbsghxjmdrxoxaevzm@forum.dlang.org#post-jhkbsghxjmdrxoxaevzm:40forum.dlang.org


This program shows two problems that make subtyping much less 
handy/useful for me in D:


// Start program -------------------------------
struct Digit {
     immutable char d;

     this(in char d_) pure nothrow
     in { assert(d_ >= '0' && d_ <= '9'); }
     body { this.d = d_; }

     alias d this;
}

U[] _validator(U, T)(in T items) pure nothrow {
     typeof(return) result;
     foreach (immutable d; items)
         result ~= U(d);
     return result;
}

template Digits(string s) {
     enum Digits = _validator!Digit(s);
}

void main() {
     import std.string: countchars;
     immutable d1 = "12341234";

     immutable n1 = countchars(d1, "23");
     assert(n1 == 4);

     // No array contravariance (through the Digit pre-condition):
     Digit[] d2 = d1; // error.

     Digit[] d3 = Digits!d1; // OK

     // No array covariance, countchars requires a
     // immutable(char)[] and refused a Digits[]:
     immutable n2 = countchars(d3, "23"); // error
     immutable n3 = countchars(d3, Digits!"23"); // error
}
// End program -------------------------------


The first problem is with array literals (here the array literals 
are strings, but a normal dynamic array of chars is a similar use 
case). If I define a subtype, like Digits, then I'd like a nice 
ways to write them in an array/associative array literal. Such 
literals should be compact, readable and yet they should be safe, 
this means if in a string meant to become a Digits[] I put a "x", 
the compiler should give me a compile-time error instead of a 
run-time error.

To solve that contravariance+validation problem here I've used a 
template and a compile-time function helper. It's not a too much 
bad solution, but maybe there are ways to improve the situation.

The other problem is that a Digit[] despite being meant as a 
subtype of char[], doesn't have covariance, so 
std.string.countchars() doesn't accept a Digit[]. This makes such 
subtyping much less useful.

A built-in annotation like this doesn't solve the problem:

@subtype alias d this;


Maybe a built-in annotation this this is enough, I don't know:

@subtype(char) struct Digit { ... }

Bye,
bearophile


More information about the Digitalmars-d mailing list