So it's no secret that Raku has multiple inheritance, so that got me wondering: "how does Raku deal with that in any reasonable manner?"
Some preliminary testing reveals that default behaviour is inherited from the first class in the inheritance list, that's fine, many other languages do it like that too
class A {
has $.foo = 0;
method speak {...}
}
class B is A {
has $.foo = 1;
method speak {
say 'moo';
}
}
class C is A {
has $.foo = 2;
method speak {
say 'baa';
}
}
class D is B is C {}
class E is C is B {}
say D.new.foo; # prints 1 (from B)
say E.new.foo; # prints 2 (from C)
But that got me wondering, what if I want D
to use C
's speak
?
Due to the inheritance order I get B's by default.
I understand that roles exist to solve this exact issue by facilitating a disambiguation mechanism, but suppose hypothetically I find myself in a situation where I don't have roles at my disposal (boss hates them, inherited a library that doesn't have them, pick your excuse) and really need to disambiguate the inherited classes.
What's the mechanism for dealing with that in Raku?
The Diamond Problem is fixed using virtual inheritance, in which the virtual keyword is used when parent classes inherit from a shared grandparent class. By doing so, only one copy of the grandparent class is made, and the object construction of the grandparent class is done by the child class.
The "diamond problem" (sometimes referred to as the "Deadly Diamond of Death") is an ambiguity that arises when two classes B and C inherit from A, and class D inherits from both B and C.
Diamond Problem in Java This leads to the ambiguity as the compiler doesn't know which superclass method to execute. Because of the diamond-shaped class diagram, it's referred to as Diamond Problem in java. The diamond problem in Java is the main reason java doesn't support multiple inheritances in classes.
The diamond problem The diamond problem occurs when two superclasses of a class have a common base class. For example, in the following diagram, the TA class gets two copies of all attributes of Person class, this causes ambiguities. For example, consider the following program.
Generally you would need to provide a tie-breaker method in the class that has the (potential) ambiguity. Fortunately, you do not have to create separate methods, as you can call specific versions of methods in a call.
So the answer to your question: Add a method speak
to class D, and have that call the speak
method from class C
:
class D {
...
method speak { self.C::speak }
}
And for methods taking parameters, take a Capture
of all parameters, and pass that on:
class D {
...
method foo(|c) { self.C::foo(|c) }
}
Note that the "c" in |c
is just an identifier, it could be any identifier. But it is sorta customary, at least with Raku core developers, to just use |c
, with the "c" standing for "capture".
Now, this will cause some overhead, as you would have an extra level of indirection. Should this turn out to be a performance issue, there's some meta-programming you can do to alias the speak
method in D
to the speak
method in C
:
class D {
...
BEGIN D.^add_method("speak",C.^find_method("speak"));
}
This will, at compile time because of the BEGIN
, add a Method
object to the D
class called speak
that is defined by the speak
method of class C
. Since this is an alias, you don't have to worry about any parameters being passed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With