(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?









Reply via email to