www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Precise overrides

reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
I suggested that before, now I’ll put it here, where it properly 
belongs.

D has two holes in its OOP system.
1. One cannot override a method with base-type parameters, even 
if those are fine w.r.t. the Liskov substitution principle.
2. It posits that if two interfaces have a method with the same 
signature, the methods equal.


```d
class Base { }
class Derived : Base { }

interface I { Base method(Derived); }

class C : I
{
     override
     Derived method(Base) => new Derived;
}
```
Here, `C.method` is covariant with `I.method`, which is the 
theoretical requirement for overriding. However, because 
overloading exists in D, this matching cannot be assumed and 
thus, the above is an error.


```d
interface I1 { void method(); }
interface I2 { void method(); }

class C : I1, I2
{
     override
     void method() { }
}
```
Here, `C.method` implements both, `I1.method` and `I2.method` 
even if those need not be related. There is no (easy) way to 
implement those separately, which means if `I1` and `I2` are not 
related and just happen to have a method with the same signature 
and return type, they’re incompatible.


Allow `override` with an optional argument that tells the 
compiler what is overridden. In the simplest case, it’s the name 
of a base class or interface:
```d
interface I1 { void method(); }
interface I2 { void method(); }

class C : I1, I2
{
     override(I1)
     void method() { }

     override(I2)
     void method() { }
}
```
In this case, narrowing the search space is enough for the lookup 
to resolve. In this case, `c.method` will not work, it’s 
ambiguous, but `c.I1.method` and `c.I2.method` can be used to 
disambiguate (this already works in D).

To make things easier, it makes sense to allow to rename the 
method; this feature exists in C++/CLI and Visual Basic .NET:
```d
interface I1 { void method(); }
interface I2 { void method(); }

class C : I1, I2
{
     override(I1.method())
     void method1() { }

     override(I2.method())
     void method2() { }
}
```
In that case, `c.method` does not even resolve (`C` has no member 
with that name), but `c.method1` and `c.method2` will, as will 
`c.I1.method` and `c.I2.method`.

Now that we can state the exact base-class or interface method we 
want to override, that allows us to also override with 
contravariant parameters (problem 1):
```d
class Base { }
class Derived : Base { }

interface I { Base method(Derived); }

class C : I
{
     override(I.method(Derived))
     Derived method(Base) => new Derived;
}
```
Nov 03
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 3 November 2025 at 12:53:45 UTC, Quirin Schroll wrote:
 I suggested that before, now I’ll put it here, where it 
 properly belongs.

 [snip]
If something like this is added, it should probably be made an error to not make the override precise in cases like this (if it's not already). Would a scheme like this also resolve issues with the diamond problem with respect to multiple class inheritance?
Nov 03
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Monday, 3 November 2025 at 14:02:40 UTC, jmh530 wrote:
 On Monday, 3 November 2025 at 12:53:45 UTC, Quirin Schroll 
 wrote:
 I suggested that before, now I’ll put it here, where it 
 properly belongs.

 [snip]
If something like this is added, it should probably be made an error to not make the override precise in cases like this (if it's not already). Would a scheme like this also resolve issues with the diamond problem with respect to multiple class inheritance?
**TL;DR:** No, it wouldn’t. The only thing an interface cannot have, but a class can, is non-static data members. That minute fact makes the diamond problem with classes is so much harder than for interfaces. Let’s say we had this: ```d class Vehicle { int maxSpeed, passengerCount; } class Car : Vehicle { float tirePressure; } class Boat : Vehicle { float draft; } class Amphibian : Car, Boat { } ``` The question is then: For an `amphibian`, is `amphibian.Car.maxSpeed` and `amphibian.Car.passengerCount` the same as `amphibian.Boat.maxSpeed` and `amphibian.Boat.passengerCount` or are they different? What you want depends on your use case, there’s obviously no one-size-fits-all answer, since reasonably the answer is different for the two members. Disallowing multiple inheritance for classes gets you around the technical question. C++ solves the technical question via virtual vs non-virtual inheritance (forcing you to put `passengerCount` in a virtual base class of `Vehicle`), i.e. _you_ tell it what it is, and it comes with a caveat: When you instantiate an object with a virtual base class, you have to initialize this base explicitly. For all of that, you need syntax. Maybe that’s why .NET, Java, and D don’t support it. One way to solve this adding `virtual` base classes, syntax to constructor-call them, and maybe even non-static data members to D: ```d class Vehicle { int maxSpeed; virtual const int passengerCount; /*…*/ } class Car : Vehicle { float tirePressure; /*…*/ } class Boat : Vehicle { float draft; /*…*/ } class Amphibian : Car, Boat { this( int passengerCount, int landMaxSpeed, int waterMaxSpeed, float tirePressure, float draft ) { Vehicle.passengerCount = passengerCount; // ! Car.this(landMaxSpeed, passengerCount/*!*/, tirePressure); Boat.this(waterMaxSpeed, passengerCount/*!*/, draft); } } ``` The difficulties are obvious: - `passengerCount` is `const`, but both the `Car` and `Boat` aspect should initialize the same value. - The constructors of `Car` and `Boat` must each initialize it in case the object is just a `Car` or just a `Boat`. One solution is that `Car` and `Boat` constructors get told implicitly if they’re supposed to initialize the `virtual` base stuff or not. If not (as in the case above), the virtual base stuff has to be initialized elsewhere and ideally beforehand―which is why C++ does virtual base class initialization always first. Technical difficulties arise: Objects are no longer contiguous. Conceptually, an `Amphibian` contains a `Car` and a `Boat` subobject, but they share the `passengerCount`, and it can only be directly adjacent to one of them. That complicates the object model. It complicates identity comparisons: ```d Vehicle v0 = new Amphibian(…); Vehicle v1 = cast(Car)v0; Vehicle v2 = cast(Boat)v0; ``` The underlying pointers of `v1` and `v2` must be different, otherwise `v1.maxSpeed` would be the same as `v2.maxSpeed` which shouldn’t be the case as one’s treated as a `Car`, the other as a `Boat`. So, how do you find out if `v1` is `v2` (which it is) if the pointers are different? In D, `is` compares pointers (sadly, I wanted the opposite). In C++, you cast both to `void*` (which always yields a pointer to the whole object) and compare those; can’t do that in D, `cast(void*)` already means “give me the underlying pointer”. In D, for diamond interfaces, you do `cast(Object)x is cast(Object)y`, but that cast would be ambiguous if we allowed multiple inheritance.
Nov 11
prev sibling next sibling parent reply Peter C <peterc gmail.com> writes:
On Monday, 3 November 2025 at 12:53:45 UTC, Quirin Schroll wrote:
 I suggested that before, now I’ll put it here, where it 
 properly belongs.

 D has two holes in its OOP system.
 1. One cannot override a method with base-type parameters, even 
 if those are fine w.r.t. the Liskov substitution principle.
 2. It posits that if two interfaces have a method with the same 
 signature, the methods equal.
 ...
I posted this elsewhere (in General), but seems appropriate here: Borrowing and idea from Carbon and Rust, you could group all the interface methods together in a dedicated block/scope using 'impl': It's nice and structured, and would scale very well for handling multiple interfaces. module myModule; safe: private: import std.stdio : writeln; interface IWorker { void start(); void stop(); int status(); } class Base { void start() { writeln("Base: starting generic process"); } void stop() { writeln("Base: stopping generic process"); } int status() { return 0; // 0 = idle } } class Derived : Base, IWorker { // Explicit interface implementation block impl(IWorker) { void start() { writeln("IWorker: initializing hardware"); } void stop() { writeln("IWorker: shutting down hardware"); } int status() { writeln("IWorker: reporting hardware status"); return 42; // pretend 42 = "active" } } } void main() { auto d = new Derived(); // Calls Base methods d.start(); // "Base: starting generic process" d.stop(); // "Base: stopping generic process" writeln(d.status()); // 0 // Calls IWorker methods IWorker w = d; w.start(); // "IWorker: initializing hardware" w.stop(); // "IWorker: shutting down hardware" writeln(w.status()); // 42 }
Nov 03
next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Monday, 3 November 2025 at 22:51:26 UTC, Peter C wrote:
 Borrowing and idea from Carbon and Rust
God I hope no one needs to see a feature copied from carbon to know it be a waste of time
Nov 04
parent reply Peter C <peterc gmail.com> writes:
On Tuesday, 4 November 2025 at 23:32:18 UTC, monkyyy wrote:
 On Monday, 3 November 2025 at 22:51:26 UTC, Peter C wrote:
 Borrowing and idea from Carbon and Rust
God I hope no one needs to see a feature copied from carbon to know it be a waste of time
Well, my proposed 'explicit interface implemenation block' works really well with existing D sytax - it looks like it belongs in D. I wish I knew more about compilers, as I would just go and do it! //.................................... module myModule; safe: private: public interface IWorker { void start(); void stop(); } public class Base { void start(){} void stop(){} } public class Derived : Base, IWorker { impl(IWorker) { void start() {} void stop() {} } private { } scopeprivate { } } //....................................
Nov 04
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 5 November 2025 at 04:06:21 UTC, Peter C wrote:
 [snip]

 //....................................

 module myModule;
  safe:
 private:

 public interface IWorker
 {
     void start();
     void stop();
 }

 public class Base
 {
     void start(){}
     void stop(){}
 }

 public class Derived : Base, IWorker
 {
     impl(IWorker)
     {
         void start() {}
         void stop() {}
     }

     private
     {

     }

     scopeprivate
     {

     }
 }

 //....................................
I don't see what the difference is between the override suggestion from the OP. The only difference is allowing override to apply to blocks. But override already works with blocks. ```d import std.stdio: writeln; class A { int foo(int x) { return x + 1;} int bar(int x) { return x + 2;} } class B : A { override int foo(int x) { return x + 2;} override int bar(int x) { return x + 3;} } class C : A { override { int foo(int x) { return x + 2;} int bar(int x) { return x + 3;} } } void main(){ B b = new B(); writeln(b.foo(2)); // prints 4 writeln(b.bar(3)); // prints 6 C c = new C(); writeln(c.foo(2)); // prints 4 writeln(c.bar(3)); // prints 6 } ```
Nov 06
parent reply Peter C <peterc gmail.com> writes:
On Thursday, 6 November 2025 at 15:07:08 UTC, jmh530 wrote:
 ...
 I don't see what the difference is between the override 
 suggestion from the OP. The only difference is allowing 
 override to apply to blocks. But override already works with 
 blocks.

 class C : A
 {
     override {
         int foo(int x) { return x + 2;}
     	int bar(int x) { return x + 3;}
     }
 }
 ....
That's interesting, I didn't know that. But even with override blocks, it is not clear what the overrides in the block relate to (other than some methods named foo and bar). Whereas, the code below, groups all methods belonging to a specific interface contract into one logical block. A developer instantly knows which methods satisfy the IWorker contract simply by looking inside impl(IWorker) In addition, this structure would be easier for IDEs and code generators to work with, as the methods needed for a contract are clearly delineated. public class Derived : Base, IWorker { impl(IWorker) // The block explicitly declares the purpose of the enclosed method { void start() {} void stop() {} } }
Nov 06
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 6 November 2025 at 20:51:50 UTC, Peter C wrote:
 On Thursday, 6 November 2025 at 15:07:08 UTC, jmh530 wrote:
 ...
 I don't see what the difference is between the override 
 suggestion from the OP. The only difference is allowing 
 override to apply to blocks. But override already works with 
 blocks.

 class C : A
 {
     override {
         int foo(int x) { return x + 2;}
     	int bar(int x) { return x + 3;}
     }
 }
 ....
That's interesting, I didn't know that. But even with override blocks, it is not clear what the overrides in the block relate to (other than some methods named foo and bar). Whereas, the code below, groups all methods belonging to a specific interface contract into one logical block. A developer instantly knows which methods satisfy the IWorker contract simply by looking inside impl(IWorker) In addition, this structure would be easier for IDEs and code generators to work with, as the methods needed for a contract are clearly delineated. public class Derived : Base, IWorker { impl(IWorker) // The block explicitly declares the purpose of the enclosed method { void start() {} void stop() {} } }
So you would mandate that the override blocks can only be within one block? I guess my point I was that naming the keyword as impl vs override is really just how to name things, I dont think it pulls its weight. And I think mandating that these need to be in one block would unnecessarily break code for little practical benefit. If creating language from scratch maybe better.
Nov 06
parent reply Peter C <peterc gmail.com> writes:
On Thursday, 6 November 2025 at 23:18:05 UTC, jmh530 wrote:
 ...
 So you would mandate that the override blocks can only be 
 within one block?

 I guess my point I was that naming the keyword as impl vs 
 override is really just how to name things, I dont think it 
 pulls its weight.

 And I think mandating that these need to be in one block would 
 unnecessarily break code for little practical benefit. If 
 creating language from scratch maybe better.
I'm not mandating anything. That is nice redirection of my post.. into a direction it never intended (explicately or implicately). Nor is my intention to implement a change that would break code ... yet another redirection. The only point I'm making, is that if I could do this in D, I'd probably use D more often than I do - which is already not that often. I like code that allows me the ability to easily reason about it, locally if possible. A couple of methods that are, or are not, marked as override (since with an interface method in D, you can do either) doesn't help me reason about the code. An explicit impl block helps me reason about the code - that is, the block contains an 'implementation for the interface specified in that impl block. This is a contraversial proposal to you? Really? If each interface (or abstract class) the class implements is in a impl block, it provides conceptual consistency. This is a contraversial proposal to you? Really? Besides that, it also helps the compiler to ensure things are being typed the way your class says it's being typed. Personally, I don't like override for abstract methods. Override should be reserved for replacing a method that already has an implementation, not for replacing a virtual method with a concerte body. The difference seems minor at first, but it's that difference that helps you reason about code more easily. Here is code I can easily reason about: interface IWorker { void start(); void stop(); } interface ILogger { void start(); void stop(); } class Base { void start() { writeln("Base: generic start"); } void stop() { writeln("Base: generic stop"); } } class Derived : Base, IWorker, ILogger { // Explicit implementation for IWorker impl(IWorker) { void start() { writeln("IWorker: starting job"); } void stop() { writeln("IWorker: stopping job"); } } // Explicit implementation for ILogger impl(ILogger) { void start() { writeln("ILogger: logging start"); } void stop() { writeln("ILogger: logging stop"); } } // I'm redefining the behavior specified in the base type override void start() { ... } } The alterative to the about in D, is .. well you try it.
Nov 06
next sibling parent Peter C <peterc gmail.com> writes:
On Friday, 7 November 2025 at 05:44:50 UTC, Peter C wrote:
 ...
 The alterative to the about in D, is .. well you try it.
So here is an alternative to the 'above' - previously known as the 'about' ;-) explicit interface implementations. The overall purpose of the code, is to show how D can achieve multiple implementation inheritance and explicit separation of concerns, without supporting an explicit interface declaration syntax. It does this primarily using templates, string mixins, and nested structs. It does actually work. But geeszzzzzz! I'd much rather I just had explicit interface declarations. module myModule; safe: private: import std.stdio; template IWorkerConcept(T) { static assert(__traits(compiles, { T t; })); } template ILoggerConcept(T) { static assert(__traits(compiles, { T t; })); } template IWorkerMixin(T) { enum string IWorkerMixinString = q{ public void start() { writeln("IWorker: starting job"); } public void stop() { writeln("IWorker: stopping job"); } }; } template ILoggerMixin(T) { enum string ILoggerMixinString = q{ public void start() { writeln("ILogger: logging start"); } public void stop() { writeln("ILogger: logging stop"); } }; } class Base { void start() { writeln("Base: generic start"); } void stop() { writeln("Base: generic stop"); } } class Derived : Base { override void start() { this.IWorker_Functions.start(); this.ILogger_Functions.start(); writeln("Derived: custom start logic"); super.start(); } override void stop() { writeln("Derived: custom stop logic"); this.IWorker_Functions.stop(); this.ILogger_Functions.stop(); super.stop(); } struct IWorker_Functions_Struct { mixin(IWorkerMixin!Derived.IWorkerMixinString); } IWorker_Functions_Struct IWorker_Functions; struct ILogger_Functions_Struct { mixin(ILoggerMixin!Derived.ILoggerMixinString); } ILogger_Functions_Struct ILogger_Functions; } void main() { Derived d = new Derived(); writeln("--- Polymorphic Call ---"); Base b = d; b.start(); writeln("\n--- Explicit IWorker Call ---"); d.IWorker_Functions.stop(); writeln("\n--- Explicit ILogger Call ---"); d.ILogger_Functions.stop(); }
Nov 06
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 7 November 2025 at 05:44:50 UTC, Peter C wrote:
 [snip]

 I'm not mandating anything. That is nice redirection of my 
 post.. into a direction it never intended (explicately or 
 implicately).
1) I asked a question and then proceeded as if the answer was yes. 2) My reasoning for this is: a) I pointed out that override blocks are already allowed and that if the OPs proposal were approved, it would be functionally the same as what you are saying (because the user could put override blocks with explicit labels in their code). The only way that they would be different is that OP's proposal doesn't seem to care if you have an individual function or blocks or whatever, and you seem to want one block for each explicit override grouping. If you don't care about this, then you would have said something like "yes, I guess they are the same" instead of statements about the importance of putting overrides in single blocks. b) You also said "In addition, this structure would be easier for IDEs and code generators to work with, as the methods needed for a contract are clearly delineated." The only way that this would be better for IDEs and code generators is if you require explicitly labeling them.
 Nor is my intention to implement a change that would break code 
 ... yet another redirection.
If you require blocks of overrides or single blocks of overrides, then yes it would break code. If you don't understand this, then I will be done with this conversation.
 The only point I'm making, is that if I could do this in D, I'd 
 probably use D more often than I do - which is already not that 
 often.

 I like code that allows me the ability to easily reason about 
 it, locally if possible.
Nothing is stopping you right now from using override blocks and putting a comment of where they are inheriting from. Maybe it makes sense to enhance this in the OPs direction, but that is the state of things.
 A couple of methods that are, or are not, marked as override 
 (since with an interface method in D, you can do either) 
 doesn't help me reason about the code.

 An explicit impl block helps me reason about the code - that 
 is, the block contains an 'implementation for the interface 
 specified in that impl block.

 This is a contraversial proposal to you? Really?
Breaking code is the controversial part of what you are suggesting.
 If each interface (or abstract class) the class implements is 
 in a impl block, it provides conceptual consistency.

 This is a contraversial proposal to you? Really?
Breaking code is the controversial part of what you are suggesting.
Nov 07
parent Peter C <peterc gmail.com> writes:
On Friday, 7 November 2025 at 14:14:07 UTC, jmh530 wrote:
 .........
Well, the whole conversation is mute really, since any proposed change to D that imposed more complex rules and type-checking within the D compiler, would almost certainly be rejected. I mean can you see Walter saying yeah, I'll put up my hand to implement this? But in general, explicit interface disambiguation is generally considered a good idea in languages that support multiple interfaces - though it comes with downsides as well. D leans towards a stronger preference for compiler simplicity. So ideas like this will likely go nowhere, fast. Maybe D's successor, 'DOOP' (D for OO programmers), will allow programmers to express more elegant OOP patterns ;-)
Nov 07
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Monday, 3 November 2025 at 22:51:26 UTC, Peter C wrote:
 On Monday, 3 November 2025 at 12:53:45 UTC, Quirin Schroll 
 wrote:
 I suggested that before, now I’ll put it here, where it 
 properly belongs.

 D has two holes in its OOP system.
 1. One cannot override a method with base-type parameters, 
 even if those are fine w.r.t. the Liskov substitution 
 principle.
 2. It posits that if two interfaces have a method with the 
 same signature, the methods equal.
 ...
I posted this elsewhere (in General), but seems appropriate here: Borrowing and idea from Carbon and Rust, you could group all the interface methods together in a dedicated block/scope using 'impl': It's nice and structured, and would scale very well for handling multiple interfaces. ```d module myModule; safe: private: import std.stdio : writeln; interface IWorker { void start(); void stop(); int status(); } class Base { void start() { writeln("Base: starting generic process"); } void stop() { writeln("Base: stopping generic process"); } int status() { return 0; // 0 = idle } } class Derived : Base, IWorker { // Explicit interface implementation block impl(IWorker) { void start() { writeln("IWorker: initializing hardware"); } void stop() { writeln("IWorker: shutting down hardware"); } int status() { writeln("IWorker: reporting hardware status"); return 42; // pretend 42 = "active" } } } void main() { auto d = new Derived(); // Calls Base methods d.start(); // "Base: starting generic process" d.stop(); // "Base: stopping generic process" writeln(d.status()); // 0 // Calls IWorker methods IWorker w = d; w.start(); // "IWorker: initializing hardware" w.stop(); // "IWorker: shutting down hardware" writeln(w.status()); // 42 } ```
That’s basically the proposal, except `impl` is `override`. It’s an attribute, it supports blocks already: ```d interface I { void f(); } class C : I { override { void f() {} } } ``` Thinking about it, I would want to make the calls ambiguous unless told otherwise. And to tell it otherwise, I’d use something like `default override(I)` or `override(I) default`. The non-default members are shadowed in a similar way symbols from a named mixed-in mixin template are hidden: ```d import std.stdio; mixin template t() { void f() { writeln("t.f"); } void g() { writeln("t.g"); } void h() { writeln("t.h"); } } mixin template u() { void h() { writeln("u.h"); } } struct S { void f() { writeln("S.f"); } mixin x = t; mixin y = u; // alias h = x.h; } void main() { S s; s.f; s.g; s.x.f; s.h; // Error: ambiguous } ``` The call to `s.f` resolves and calls `S.f` because `t.f` is shadowed/hidden/… (call it what you want) but it’s still there. `t.g` is in the same spot, but not shadowed by a symbol in `S`, so it’s callable directly. The call `s.h` is ambiguous because it refers to one of two hidden members; an `alias` can be used to resolve it. I don’t see how precise overrides couldn’t work in the same spirit.
Nov 11
prev sibling next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 04/11/2025 1:53 AM, Quirin Schroll wrote:
 I suggested that before, now I’ll put it here, where it properly belongs.
 
 D has two holes in its OOP system.
 1. One cannot override a method with base-type parameters, even if those 
 are fine w.r.t. the Liskov substitution principle.
 2. It posits that if two interfaces have a method with the same 
 signature, the methods equal.
 

 ```d
 class Base { }
 class Derived : Base { }
 
 interface I { Base method(Derived); }
 
 class C : I
 {
      override
      Derived method(Base) => new Derived;
 }
 ```
 Here, `C.method` is covariant with `I.method`, which is the theoretical 
 requirement for overriding. However, because overloading exists in D, 
 this matching cannot be assumed and thus, the above is an error.
I have double checked the vtable offsets, and they do not change between Base and Derived. ```d import std.stdio; class Base { void method1() {} void method2() {} } class Derived : Base { override void method2() {} void method3() {} override void method1() {} } void main() { writeln(__traits(getVirtualIndex, Base.method1)); writeln(__traits(getVirtualIndex, Base.method2)); writeln(__traits(getVirtualIndex, Derived.method1)); writeln(__traits(getVirtualIndex, Derived.method2)); } ``` As an implementation detail this does track and would be safe to do.

 ```d
 interface I1 { void method(); }
 interface I2 { void method(); }
 
 class C : I1, I2
 {
      override
      void method() { }
 }
 ```
 Here, `C.method` implements both, `I1.method` and `I2.method` even if 
 those need not be related. There is no (easy) way to implement those 
 separately, which means if `I1` and `I2` are not related and just happen 
 to have a method with the same signature and return type, they’re 
 incompatible.
This is intentional. This is the diamond problem without the parent having an implementation.

 Allow `override` with an optional argument that tells the compiler what 
 is overridden. In the simplest case, it’s the name of a base class or 
 interface:
 ```d
 interface I1 { void method(); }
 interface I2 { void method(); }
 
 class C : I1, I2
 {
      override(I1)
      void method() { }
 
      override(I2)
      void method() { }
 }
 ```
 In this case, narrowing the search space is enough for the lookup to 
 resolve. In this case, `c.method` will not work, it’s ambiguous, but 
 `c.I1.method` and `c.I2.method` can be used to disambiguate (this 
 already works in D).
If you need extra syntax to differentiate you might as well use composition. The changes to add the namespace to symbol lookup is a little eye raising and I am confident would have enough push back that it won't be an accepted solution. Is there an example where such a solution which was not possible to solve and composition would not be an appropriate choice?
Nov 06
next sibling parent Peter C <peterc gmail.com> writes:
On Thursday, 6 November 2025 at 16:33:04 UTC, Richard (Rikki) 
Andrew Cattermole wrote:
...
 Is there an example where such a solution which was not 
 possible to solve and composition would not be an appropriate 
 choice?
Yeah, I'm sure Microsoft are saying .. "you know, we never really needed strong typing in .NET. We could have just used duck-typed composition."
Nov 06
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 6 November 2025 at 16:33:04 UTC, Richard (Rikki) 
Andrew Cattermole wrote:
 On 04/11/2025 1:53 AM, Quirin Schroll wrote:

 ```d
 interface I1 { void method(); }
 interface I2 { void method(); }
 
 class C : I1, I2
 {
      override
      void method() { }
 }
 ```
 Here, `C.method` implements both, `I1.method` and `I2.method` 
 even if those need not be related. There is no (easy) way to 
 implement those separately, which means if `I1` and `I2` are 
 not related and just happen to have a method with the same 
 signature and return type, they’re incompatible.
This is intentional.
I don’t think it is. It defeats the whole purpose of interfaces being explicit and not duck typing.
 This is the diamond problem without the parent having an 
 implementation.
Is it? I thought the diamond problem was implementing the same interface twice via different routes, i.e.: ```d interface I {…} interface I1 : I {…} interface I2 : I {…} interface J : I1, I2 {…} ``` Everything implementing `J` has `I` available twice through `I1` and `I2`. The “problem” part is: Lead these conceptually to the same thing or should they be regarded as different? It’s a real question with differing answers depending on the problem at hand. C++ solves this by letting you choose (which is good), and most (all?) other OO programming languages (incl. D) make assumptions that, when wrong, are annoying to work around. The question “Are `I.f` and `J.f` conceptually the same?” has an easy answer and it’s the one to the question: “Is one of them derived from the other?”

 Allow `override` with an optional argument that tells the 
 compiler what is overridden. In the simplest case, it’s the 
 name of a base class or interface:
 ```d
 interface I1 { void method(); }
 interface I2 { void method(); }
 
 class C : I1, I2
 {
      override(I1)
      void method() { }
 
      override(I2)
      void method() { }
 }
 ```
 In this case, narrowing the search space is enough for the 
 lookup to resolve. In this case, `c.method` will not work, 
 it’s ambiguous, but `c.I1.method` and `c.I2.method` can be 
 used to disambiguate (this already works in D).
If you need extra syntax to differentiate you might as well use composition. The changes to add the namespace to symbol lookup is a little eye raising and I am confident would have enough push back that it won't be an accepted solution. Is there an example where such a solution which was not possible to solve and composition would not be an appropriate choice?
From a high-level perspective, composition is always incorrect when it’s “X is a Y” and not “X has a Y.” From a low-level perspective, there is a subobject either way. Composition requires an additional dereference. From a practical perspective, in D, a class implicitly converts to is bases and implemented interfaces, and if it uses composition, it can only do so once (only one `alias this` allowed). I just ran into this problem in Java many years ago and was surprised when I implemented an `IEnumerable(Of T)` in Visual Basic .NET later and found out that it requires solving this
Nov 11
prev sibling parent Atila Neves <atila.neves gmail.com> writes:
On Monday, 3 November 2025 at 12:53:45 UTC, Quirin Schroll wrote:
 I suggested that before, now I’ll put it here, where it 
 properly belongs.

 D has two holes in its OOP system.
 1. One cannot override a method with base-type parameters, even 
 if those are fine w.r.t. the Liskov substitution principle.
 2. It posits that if two interfaces have a method with the same 
 signature, the methods equal.


 ```d
 class Base { }
 class Derived : Base { }

 interface I { Base method(Derived); }

 class C : I
 {
     override
     Derived method(Base) => new Derived;
 }
 ```
 Here, `C.method` is covariant with `I.method`, which is the 
 theoretical requirement for overriding. However, because 
 overloading exists in D, this matching cannot be assumed and 
 thus, the above is an error.
Is this a problem? Compared to this, anyway? ``` class C : I { override Base method(Base) => new Derived; } ```
Nov 11