www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Troubles with user defined subtypes

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 { ... }

Dec 17 2012