General problem I'm having in D with the type system

IntegratedDimensions IntegratedDimensions at gmail.com
Sun May 27 06:00:30 UTC 2018


(see my other most recent post for disclaimer)

My designs generally work like this:

Main Type   uses   Subservient types
    A                      a
    B                      b
    C                      c


where C : B : A, c : b : a.

In the usage I must also keep consistent the use of c in C, b in 
B, a in A. There is little if nothing in the type system that 
allows me to specify and force this type of relationship. 
Although there is some covariant functions that do help such as 
overriding properties with covariance.

class a;
class b : a;


class A { a x; @property a X() { return x; } @property void X(a 
v) { x = v; } }
class _B { b x; @property b X() { return x; } @property void X(a 
v) { x = v; } }
class B : A { @property b X() { return cast(b)x; } @property void 
X(b v) { x = v; } }


Note that class _B is basically that of A which changes the type 
a in to a b but otherwise is identical. This is to prove a point 
of relationship in that _B uses b just as B should.

Class B tries to treat x as a type b as much as possible. IN 
fact, by design, x is always assigned a b.

In this case, the design is safe by effort rather than type 
consistency.

A f = new B();

f.x is a type of b which is of type a, so no violations here.

B g = new B();

g.x is of b type of so no violations.

but note that

f.x and g.x both accept type a. Of course g.X enforces type 
safety.


Effectively the subservient types always grow with the main types 
in parallel so they never get out of step. In category theory 
this is equivalent to a natural transformation.

A -> a
|    |
v    v
B -> b



Ideally one simply should express this in a meaningful way:

class B : A
{
   extend b : a x;
   @property b X() { x; } // note that we do not need a cast
   @property void X(b v) { x = v; }
}


the syntax tells the compile that x of type a in A is of type b 
in B and that b must extend a. This then gives us something more 
proper to _B.


Note that now

B g = new B();

g.x = new a(); // is invalid g.x is forced to be b which is 
derived from a(or anything derived from b)




These designs are very useful because they allow a type and all 
it's dependencies to be extended together in a "parallel" and 
keep type consistency. Natural transformations are extremely 
important in specify structural integrity between related types. 
While D allows this using "hacks"(well, in fact I have yet to get 
the setter to properly work but it is not necessary because of 
direct setting and avoiding the property setter).

This is a potential suggestion for including such a feature in 
the D language to provide sightly more consistency.


Here is a "real world"(yeah, right!) example:

class food;
class catfood;

class animal { food f; }
class cat : animal { catfood : food f; }


animal  ->   food
   |            |
   v            v
cat     ->   catfood


Of course, I'm not sure how to avoid the problem in D of


animal a = new cat();

a.f = new food()
auto c = cast(cat)a;


as now f in cat will be food rather than catfood.

The cast may have to be applied for the subservient types too 
internally. (which the compiler can do internally) but should the 
main object be set to null or just the subservient object?

auto c = cast(cat)a; // if cast(cat) is extended should c be null 
or just c.f? The first case is safer but harder to fix and is the 
nuke option. In this case one might require two casting methods

auto c1 = cast(cat)a; // c1 is null
auto c2 = ncast(cat)a; // c2 is not null, c2.f is null




Thoughts, ideas?











More information about the Digitalmars-d mailing list