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

2018-05-27 Thread Nick Sabalausky (Abscissa) via Digitalmars-d

On 05/27/2018 04:50 PM, IntegratedDimensions wrote:

On Sunday, 27 May 2018 at 18:16:25 UTC, JN wrote:

On Sunday, 27 May 2018 at 06:00:30 UTC, IntegratedDimensions wrote:

animal a = new cat();

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


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


I think the problem is in your hierarchy. If Animal can have Food, 
that means that any animal should be able to accept any food, without 
knowing what kind of food is this. Cat requiring cat food is a leaky 
abstraction, the cat shouldn't know nor care what kind of food it 
gets, as it's an animal and it will eat any food.


This is clearly false.

A Kola does not eat any type of food, nor does a whale or most animals.



Exactly. That's why your hierarchy is wrong. If any Animal COULD eat any 
food, THEN you would want the Animal class to contain a Food object. 
Clearly that's not the case. Clearly, any Animal CANNOT eat any food, 
therefore the Animal class should not contain a Food object. At least, I 
think that's what JN was saying.


Of course, removing Food from the Animal class *does* cause other 
problems: You can no longer use OOP polymorphism to tell any arbitrary 
Animal to eat.


(Part of the problem here is that using base classes inherently involves 
type erasure, which then makes a mess of things.)


Honestly though, the real problem here is using class hierarchies for 
this at all.


Yea, I know, OOP was long held up as a holy grail. The one right way to 
do everything. But it turned out class hierarchies have a lot of 
problems. And you're hitting exactly one such problem: There's many 
modelling scenarios they just don't fit particularly well. (And this 
isn't a D problem, this is just OOP class hierarchies in general.)


D has plenty of other good tools, so it's become more and more common to 
just avoid all that class inheritance. Instead of getting polymorphism 
from class inheritance, get it from templates and/or delegates. Things 
tend to work better that way: it's more flexible AND gives better type 
safety because it doesn't necessitate type erasure.


This does involve approaching things a little bit differently: Instead 
of thinking/modelling in terms of nouns, think more in terms of verbs. 
It's all about what you're *doing*, not what you're modelling. And 
prefer designing things using composition ("has a") over inheritance 
("is a").


So, regarding the Animal/Food/Cat/CatFood example, here is how I would 
approach it:


What's something our program needs to do? For one, it needs to feed an 
animal:


void feedAnimal(...) {...}

But in order to do that, it needs an animal and a food:

void feedAnimal(Animal animal, Food food) {
animal.eat(food);
}

That's still incomplete. What exactly are these Animal and Food types? 
We haven't made them yet.


There are different kinds of animal and different kinds of food. We have 
two main options for handling different kinds of Animal and Food, each 
with their pros and cons: There can be a single Animal/Food type that 
knows what kind of animal, or each kind of animal/food can be a separate 
type.


A. Single types (many other ways to do this, too):

struct Animal {
enum Kind { Cat, Dog, Bird }
Kind kind;

// Eating is overridable:
void delegate(Food) customEat;

void eat(Food food) {
enforce(food.kind == this.kind, "Wrong kind of food!");

if(customEat !is null)
customEat(food);
else
writeln("Munch, munch");
}

Food preferredFood() {
return Food(kind);
}
}

struct Food {
Animal.Kind kind;
}

Animal cat() {
return Animal(Animal.Kind.Cat, (Food f){ writeln("Munch, 
meow"); });

}
Animal dog() {
return Animal(Animal.Kind.Dog, (Food f){ writeln("Munch, 
woof"); });

}
Animal bird() {...}

Animal[] zoo;

B. Separate types (many other ways to do this, too):

import std.traits : isInstanceOf;
import std.variant : Variant;

struct Cat {
void eat(Food!Cat) { writeln("Munch, meow"); }
}
struct Dog {
void eat(Food!Dog) { writeln("Munch, woof"); }
}
struct Bird {...}

enum isAnimal(T) =
/+ however you want to determine whether a type is Animal +/

struct Food(Animal) if(isAnimal!Animal) {}
enum isFood(T) = isInstanceOf!(Food, T);

void feedAnimal(Animal, Food)(Animal animal, Food food)
if(isAnimal!Animal && isFood!Food)
{
// Compiler gives error if it's the wrong food:
animal.eat(food);
}

Variant[] zoo;


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

2018-05-27 Thread IntegratedDimensions via Digitalmars-d

On Sunday, 27 May 2018 at 21:16:46 UTC, Neia Neutuladh wrote:
On Sunday, 27 May 2018 at 20:50:14 UTC, IntegratedDimensions 
wrote:
The only problem where it can leak is when we treat an cat as 
an animal then put in dog food in to the animal, which is 
valid when cat as treated as an animal, then cast back to cat. 
Now cat has dog food, which is invalid.


It sounds like you don't want to have a `food` setter in the 
`Animal` base class. Instead, you want setters in each child 
class that take the specific type required, and possibly an 
abstract getter in the base class. You could use a mixin to 
ease the process of defining appropriate food types, and you 
could have a method that takes the base `Food` class and does 
runtime validation.


You might also change `Animal` to `Animal!(TFood : Food)` and 
go from there. You'd likely need to extract another base class 
or interface so you can have a collection of arbitrary animals.


The problem with all this is that it is not the correct way. For 
N types you have to scale: Animal(Food, Skin, Eyes, ...).


While not having a specific setter in the Animal class does solve 
the problem of preventing assignment and sorta solve the problem 
by requiring assignment to occur at the proper object it does not 
solve the general problem. If we create an animal we should we 
able to assign it animal food(rather than tools). In a sense this 
goes a bit too far. Remember, a type hierarchy could be much more 
complex and we can have types of types which will then all have 
to follow these "workarounds" resulting if a very complex mess.


life
   domain
  kingdom
 phylum
class
   order
  family
 genus
species
   ...
Within each of these types there are specializations

the human species is a type of species in the homo genus, etc.

Now, how can we model such a thing be providing maximum compile 
time typing structure?


class life; // Sorta like object, something that can be anything 
and contains only the common structure to all things that can be 
considered living


class domain : life;
...

class species : genus;
class human : species;

Now, this is all standard. But all types will *use* other types. 
Humans will use, for example, tools. So we will have another 
complex hierarchy where tools fall somewhere


class tool : object;
class hammer : tool;


so

class human : species
{
tool[] t;
}


Now, suppose we have bob the house builder, a human,

class houseBuilder : humanWorker;

in which we could add a tool to bob

auto bob = new houseBuilder();
bob.tools ~= new hammer();

All fine an dandy!


This is because tool there is not a natural transformation 
between human and houseBuilder and tool and hammer. There a 
hammer being derived from a tool in no way corresponds to a 
houseBuilder being derived from a human. The "uses" in this case 
is from types to types and objects to objects (humans use tools 
and bob uses a hammer).


For the structure I am talking about, "parallel inheritance" 
there is a natural relationship between types to types and types 
to types that naturally transform in "parallel".


To understand this we need to think about something that 
parallels our taxonomy so that as we inherit through one there is 
a *natural* inheritance through the other and a sort of 
"correspondence"(the natural transformation) that keeps 
everything aligned.  Sorta like a ladder where we can only move 
up and down but each end of a rung.


Life ->A
Domain   ->B
...  ->...
Genus->X
Species  ->Y
Human->Z
houseBuilder ->_

Now, if your paying attention, Human is actually not part of the 
taxonomy above, it is a specialization of Species.


Unfortunately in D we only have one level of typing rather that 
types of types. We only have a type. The taxonomy above would be 
a type of a type of an object while humans would be a type of 
object. So what happens is the different conceptualizations are 
conflated. Since types can be treated as sets, this is like 
saying that sets of sets are the same as sets. Well, sets of sets 
are sets but not all sets are sets of sets, hence they are not 
exactly the same(it is inclusion rather than equality).


{1,2,3} is not a set of sets. (although, it is true we can treat 
1,2,3 as sets and so we could say it is but but I don't want to 
get in to the sets of things that are not sets problem).


So, really what we have is that the taxonomy is precisely this 
paralleling that goes on:


Life ->Life
Domain   ->Eukaryote
...  ->......   ...
Genus->Homo  _>   ...   ...
Species  ->Human _>   houseBuilder  --> bob   --> 
hammer




So, for example, Eukaryote's have cells that build things and 
different ways to class them. Hence "Builders" would have hav

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

2018-05-27 Thread David Nadlinger via Digitalmars-d
On Sunday, 27 May 2018 at 06:00:30 UTC, IntegratedDimensions 
wrote:
[…] This is a potential suggestion for including such a feature 
in the D language to provide sightly more consistency.


Solving this in the general case requires explicitly allowing, 
specifying, and tracking covariance and contravariance relations 
throughout the type system.


Object-oriented programming in the usual sense only models arrows 
in one direction – covariance of return types (and possibly 
contravariant argument types). As far as I'm aware, there is 
little more useful to be done without making both compile-time 
and run-time representations (i.e., type system and memory 
layout) significantly more complex.



The only problem where it can leak is when we treat an cat as 
an animal then put in dog food in to the animal, which is valid 
when cat as treated as an animal, then cast back to cat. […] 
But I've already pointed out two things about this: 1. The 
hierarchy is maintained to be non-leaky at runtime(I never down 
cast). […]


Even without explicit downcasts, there are still implicit upcasts 
and covariant `this` pointers. Consider this:


---
class Food;
class Catfood : Food;

class Animal { Food f; void eat() { /+ eat f +/ } }
class Cat : Animal { Catfood : Food f; override void eat() { /+ 
eat f +/ } }


Animal a = new Cat;
a.f = new Food;
a.eat(); // calls Cat.eat()
---

As per your problem statement, given a (this) pointer of type Cat 
– such as in Cat.eat() –, you'd presumably want `f` to be of type 
Catfood. However, as shown, this is incompatible with Cat being a 
subtype of Animal.




Haskell can handle these situations just fine.


Haskell also (mostly) lacks subtyping and mutable data.

–––

In general, this is a fun problem to think about – at least if 
one does not expect to (quickly) come up with generic solutions. 
Given your Haskell background, you might enjoy having a look at 
what Scala and others have done with generics for an illustration 
of what makes this tricky in an OOP environment.


As for D, I'd recommend taking a closer look at what your actual 
design requirements are. If you don't require the full 
flexibility of arbitrary co-/contravariance relations on an 
unrestricted set of types, it is likely that you can brew your 
own subtyping system with template magic to provide exactly the 
required structure and behaviour. Template application is 
invariant, so you have full freedom to allow precisely those 
conversions you want to exist. The implementation will involve 
structs that internally cast unrelated pointers, but that can be 
hidden from the interface. You can always package this up as a 
library if you succeed in making it generally useful.


 — David


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

2018-05-27 Thread IntegratedDimensions via Digitalmars-d

On Sunday, 27 May 2018 at 06:59:43 UTC, Vijay Nayar wrote:
On Sunday, 27 May 2018 at 06:00:30 UTC, IntegratedDimensions 
wrote:



The problem description is not very clear, but the catfood 
example gives a bit more to work with.



animal  ->   food
  ||
  vv
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;


Cast operations are generally not guaranteed to preserve type 
safety and should be avoided when possible.  But if I 
understand your description, you have the following relations 
and transitions:


  animal owns food
  catowns catfood
  animal may be treated as a cat (hence the casting)
  food may be treated as a catfood (hence the casting)

It may be that the inheritance relationship is backwards in 
your use case.  If "animal" may be treated as a "cat", then the 
inheritance should be other other way around, and "animal" 
would inherit from "cat".


No, this would make no sense. Inheritance is about 
specialization, taking a type and specifying more constraints or 
properties to make it more well defined or more specific. Or, 
simply, a superset.


What specific kinds of relationships are you trying to model 
among what kinds of entities?


I've already mentioned this. It is natural for specializations of 
a type to also specialize dependencies. The animal/cat example is 
valid.


A animal can be any thing that is an animal that eats any food.
a cat is an animal that is a cat and eats only food that cats eat.

This is true, is it not? cats may eat dog food, it is true, but 
cats do not eat any animal food. They do specialize. Cat food may 
be less specialized to some in between thing for this specific 
case to work but it's only because I used the term cat food 
rather than some other more general class.


Animal -> Animal food
Koala   -> Koala food

A Koala only eats specific types of food, nothing else. We can 
call that Koala food.


As an animal, koala food is still animal food, so casting still 
works. It is only the upcasting that can fail. But that is true 
in general(we can't cast all animals to Koala's... and similarly 
we can't cast all animal food to all Koala food). D's cast will 
only enforce one side because he does not have the logic deal 
with dependent parallel types.


This is a very natural thing to do. Haskell can handle these 
situations just fine. With D, and it's inability to specify the 
relationship dependencies, it does not understand that things are 
more complex.


Hence we can, in D, put any type of food in Koala in violation of 
the natural transformations we want:


(cast(Animal)koala).food = catFood;

This is a violation of the structure but allowable in D due to it 
not being informed we cannot do this. If we had some way to 
specify the structure then it would result in a runtime 
error(possibly compile time if it new koala was a Koala and could 
see that we are trying to assign catFood to a KoalaFood type).



auto a = (cast(Animal)koala);
a.food = someFood;
auto k = cast(Koala)a;
k.food =?= someFood; // error

of course, if cast worked using deeper structural logic then 
k.food would be null or possibly k would be null(completely 
invalid cast).


You have to realize that I am talking about applying constraints 
on the type deduction system that do not already exist but that 
actually make sense.


If you wanted to model the animal kingdom and made a list of all 
the animals and all the food they ate, there would be 
relationships. Some animals will eat just about anything while 
others will eat only one thing.


AnimalsFoods
 ......

If you were to model this in using classes you would want some 
way to keep some consistency.


If you do

class Animal
{
Food food;
}

class Koala : Animal
{

}


Then Koala allows *any* food... then you have to be careful of 
sticking in only koala food! But if we *could* inform the 
compiler that we have an additional constraint:


class SmartKoala : Animal
{
KoalaFood : Food food;
}


then SmartKoala will be able to prevent more compile time errors 
and enforce the natural inherence relationship that exists 
between animals on food.


We can do this with properties on some level

class Animal
{
@property void food(Food food);
}

class SemiSmartKoala : Animal
{
override @property void food(Food food) { if 
(!is(typeof(food) == KoalaFood)) throw ... }

}


This, of course, only saves us at runtime and is much more 
verbose and is not really naturally constraining dependencies.















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

2018-05-27 Thread Neia Neutuladh via Digitalmars-d
On Sunday, 27 May 2018 at 20:50:14 UTC, IntegratedDimensions 
wrote:
The only problem where it can leak is when we treat an cat as 
an animal then put in dog food in to the animal, which is valid 
when cat as treated as an animal, then cast back to cat. Now 
cat has dog food, which is invalid.


It sounds like you don't want to have a `food` setter in the 
`Animal` base class. Instead, you want setters in each child 
class that take the specific type required, and possibly an 
abstract getter in the base class. You could use a mixin to ease 
the process of defining appropriate food types, and you could 
have a method that takes the base `Food` class and does runtime 
validation.


You might also change `Animal` to `Animal!(TFood : Food)` and go 
from there. You'd likely need to extract another base class or 
interface so you can have a collection of arbitrary animals.


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

2018-05-27 Thread Basile B. via Digitalmars-d
On Sunday, 27 May 2018 at 06:00:30 UTC, IntegratedDimensions 
wrote:

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


1/ I think that signatures could solve this problem in a nice way 
(https://github.com/rikkimax/DIPs/blob/master/DIPs/DIP1xxx-RC.md).


2/ For now you can solve the problem with a "template this" 
parameter. This is not a perfectly clean solution but not an ugly 
one either. You don't need to rewrite the x setter and getter and 
in order to cast because you get the most derived type in the 
calling context (although the compiler still does a dynamic cast, 
but not for the "parallel type", which is provided by a "mixin 
template").


```
module runnable;

class a {}
class b : a {}
class c : b {}

mixin template Extensible(E)
if (is(E : a))
{
alias Extended = E;
Extended _x;
this()
{
_x = new Extended;
}
}

class A
{
mixin Extensible!a;
@property auto x(this T)() { return (cast(T) this)._x; }
@property void x(this T, V)(V v) { (cast(T) this)._x = v; }
}

class B : A
{
mixin Extensible!b; // extend b : a x;
}

class C : B
{
mixin Extensible!c; // extend c : b x;
}

void main()
{
B someB = new B;
C someC = new C;
static assert(is(typeof(someB.x()) == b));
static assert(is(typeof(someC.x()) == c));
}
```

What's not nice is that even if the "template this" parameter 
allows to select the right x, there's still an instance of x for 
each sub 

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

2018-05-27 Thread IntegratedDimensions via Digitalmars-d

On Sunday, 27 May 2018 at 18:16:25 UTC, JN wrote:
On Sunday, 27 May 2018 at 06:00:30 UTC, IntegratedDimensions 
wrote:

animal a = new cat();

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


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


I think the problem is in your hierarchy. If Animal can have 
Food, that means that any animal should be able to accept any 
food, without knowing what kind of food is this. Cat requiring 
cat food is a leaky abstraction, the cat shouldn't know nor 
care what kind of food it gets, as it's an animal and it will 
eat any food.


This is clearly false.

A Kola does not eat any type of food, nor does a whale or most 
animals.


While it is just an example, it still applies in general. Natural 
transformations are fundamental to type theory.



The only problem where it can leak is when we treat an cat as an 
animal then put in dog food in to the animal, which is valid when 
cat as treated as an animal, then cast back to cat. Now cat has 
dog food, which is invalid.


But I've already pointed out two things about this: 1. The 
hierarchy is maintained to be non-leaky at runtime(I never down 
cast). 2. casting can be designed to handle this behavior. When 
we cast animal to cat then the cast can null dog food since it 
isn't cat food. No different than if we cast animal to hammer but 
requires a bit more compiler logic.









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

2018-05-27 Thread JN via Digitalmars-d
On Sunday, 27 May 2018 at 06:00:30 UTC, IntegratedDimensions 
wrote:

animal a = new cat();

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


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


I think the problem is in your hierarchy. If Animal can have 
Food, that means that any animal should be able to accept any 
food, without knowing what kind of food is this. Cat requiring 
cat food is a leaky abstraction, the cat shouldn't know nor care 
what kind of food it gets, as it's an animal and it will eat any 
food.


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

2018-05-27 Thread Vijay Nayar via Digitalmars-d
On Sunday, 27 May 2018 at 06:00:30 UTC, IntegratedDimensions 
wrote:



The problem description is not very clear, but the catfood 
example gives a bit more to work with.



animal  ->   food
  ||
  vv
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;


Cast operations are generally not guaranteed to preserve type 
safety and should be avoided when possible.  But if I understand 
your description, you have the following relations and 
transitions:


  animal owns food
  catowns catfood
  animal may be treated as a cat (hence the casting)
  food may be treated as a catfood (hence the casting)

It may be that the inheritance relationship is backwards in your 
use case.  If "animal" may be treated as a "cat", then the 
inheritance should be other other way around, and "animal" would 
inherit from "cat".


What specific kinds of relationships are you trying to model 
among what kinds of entities?




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

2018-05-26 Thread IntegratedDimensions via Digitalmars-d

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