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

David Nadlinger code at klickverbot.at
Sun May 27 22:23:02 UTC 2018


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


More information about the Digitalmars-d mailing list