Course of Raku / Addendum 🆕 / Types and text machines / Objects and classes / Exercises / Shapes sharing a role
Solution: Shapes sharing a role
Here is a possible solution to the task.
Code
role Shape {
method area { ... }
}
class Circle does Shape {
has $.radius;
method area { 3.14159 * $.radius ** 2 }
}
class Square does Shape {
has $.side;
method area { $.side ** 2 }
}
sub describe(Shape $shape) {
say "{$shape.^name}: {$shape.area}";
}
for Circle.new(radius => 2), Square.new(side => 3) -> $shape {
describe($shape);
}🦋 You can find the source code in the file shapes-role.raku.
Output
Circle: 12.56636
Square: 9Comments
Strictly, the call
$shape.areawould work without the role at all. Raku resolves methods by name at the moment of the call, so as long as each object happens to have anareamethod, the loop runs — this is known as duck typing. So what does the role actually buy us here? Two guarantees.The role is a type you can require.
describeis declared assub describe(Shape $shape), so it accepts only objects that doShapeand rejects anything else before the body runs. Pass it a plain number and the program refuses to compile:describe(42); # Calling describe(Int) will never work with declared signature (Shape $shape)
Without the role there is no such type — a duck-typed routine would take any argument and only blow up later, deep inside, when it reaches
.area.The role is a contract. Declaring
areaas a stub (method area { ... }) forces every class that does the role to supply its ownarea. Forget it, and the mistake cannot go unnoticed:class Triangle does Shape { has $.base; has $.height; } # Method 'area' must be implemented by Triangle because it is required by roles: Shape. my $t = Triangle.new(base => 3, height => 4); say $t.area; # Stub code executed
Rakudo reports the missing method as soon as the class is composed — the program refuses to compile before a single
Triangleobject exists. And even in an implementation that accepts the class definition, the mistake surfaces at the latest when the method is used: the stub{ ... }inherited from the role is real code that dies withStub code executedas soon as anyone calls it.$shape.^nameasks the object for its class name, so the samedescribelabels each result correctly without knowing the type in advance.