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

Reply via email to