I have this class which subclasses Str:
use Vimwiki::File::TextProcessingClasses;
unit class Vimwiki::File::ContentStr is Str;
method new(Str:D $string) {
self.Str::new(value => $string);
}
# this method fails
method capitalize-headers() {
self = Vimwiki::File::TextProcessingClasses::HeadlineCapitalizer.new.capitalize-headers(self);
}
The problem is the capitalize-headers method fails with the Cannot modify an immutable error because it’s a string. One way I could avoid this problem is to simply not subclass Str and write wrappers for the Str methods I want to use like this:
unit class Vimwiki::File::ContentStr;
has Str $!content;
method uc() {
$!content = $!content.uc;
}
But that got me wondering if instead of writing these wrapper method, there might be something like AUTOLOAD in Raku so that if a method does not exist, the non-existent method could default to being called on the $!content attribute.
Is this possible? Or is there a cleaner way around the immutable object problem?
>Solution :
The Str type is immutable. When you write:
my $x = "string";
$x = "another string";
It works because $x is a Scalar container. You’re not changing the Str, just arranging for $x to refer to a different Str. That Str is immutable is relied on all over the place in the Raku standard library, so even if you were to somehow create a mutable subclass, you’re not in for a good time!
Further to that self is also always immutable. You can do this:
class SubStr is Str {
method capitalize-inplace($self is rw:) {
$self .= uc
}
}
And use it as follows:
my $x = SubStr.new(value => "hi");
$x.capitalize-inplace;
say $x
This is is, again, mutating the Scalar container $x and putting a new SubStr instance with the value HI into it. Thus this:
SubStr.new(value => "hi").capitalize-inplace
Will die:
Parameter '$self' expects a writable container (variable) as an
argument, but got 'hi' (SubStr) as a value without a container.
in method capitalize-inplace at -e line 1
in block <unit> at -e line 1
Thus if you want a mutable wrapper around Str, you’ll really have to use composition, not inheritance.
But that got me wondering if instead of writing these wrapper method, there might be something like AUTOLOAD in Raku so that if a method does not exist, the non-existent method could default to being called on the $!content attribute.
That’s the FALLBACK method. (There’s also the handles trait, but that’s not quite what you want, as I imagine you wish to retain the wrapping.)