problem-solving
problem-solving copied to clipboard
Should the `handles` trait delegate by name or by code reference?
When handles delegates via a method call there are two approaches:
- the original one we used for years was by delegating via method name: `self."$meth_name"()."$delegation_name"()
- the one I introduced with my fix for roles uses
&method_code_object(self)."$delegation_name"()
There are a number of problems about the former approach. First, it makes it possible for a child to override the handling method and break the delegation by returning an object of different type:
class C1 {
method dt handles<year month day> { DateTime.now }
}
class C2 is C1 {
method dt { "something different" }
}
say C2.new.year; # 2022 on the master, dies on 2022.06
Second, the new approach allows for anonymous methods to do delegation too:
class Foo { }
Foo.^add_method("dt", anon method () handles<year> { DateTime.now });
say Foo.year; # Dies on 2022.06
Third, multi-dependent delegation works the way it is expected to:
class C {
multi method foo(::?CLASS:U:) handles<year> { DateTime.now }
multi method foo(::?CLASS:D:) handles<Int> { "42" }
}
# Weird "No such method 'Int' for invocant of type 'DateTime'" on 2022.06,
# and more correct "Invocant of method 'foo' must be an object instance of type 'C', not a type object of type 'C'.
# Did you forget a '.new'?" on the master
say C.Int;
Both approaches pass specs.
The only problem is the code object approach breaks CSS and LibXML due to some of their child classes overriding a method with an attribute of the same name.
Related is rakudo/rakudo#5006
I tried to make the code object behavior 6.e feature, while preserving the name-based one in 6.c/d. Seems to be working nicely, but it is too late today to release it.