Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Working around for the `Cannot modify an immutable` in my subclass of Str?

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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.)

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading