解决我的 Str 子类中的“无法修改不可变”问题?

Ste*_*ieD 3 raku

我有这个类的子类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);
}

Run Code Online (Sandbox Code Playgroud)

问题是该capitalize-headers方法因错误而失败,Cannot modify an immutable因为它是一个字符串。我可以避免这个问题的一种方法是简单地不子类化Str并为Str我想要使用的方法编写包装器,如下所示:

unit class Vimwiki::File::ContentStr;

has Str $!content;

method uc() {
   $!content = $!content.uc; 
}

Run Code Online (Sandbox Code Playgroud)

但这让我想知道,Raku 中是否可能有类似 AUTOLOAD 之类的东西,而不是编写这些包装方法,这样如果方法不存在,则可以默认在属性上调用不存在的方法$!content

这可能吗?或者是否有更干净的方法来解决不可变对象问题?

Jon*_*ton 7

类型Str是不可变的。当你写:

my $x = "string";
$x = "another string";
Run Code Online (Sandbox Code Playgroud)

它之所以有效,是因为它$x是一个Scalar容器。您没有更改Str,只是安排$x引用不同的Str. StrRaku 标准库中到处都依赖于不可变,因此即使您以某种方式创建可变子类,您也不会过得很愉快!

除此之外,它self也始终是不可变的。你可以这样做:

class SubStr is Str {
    method capitalize-inplace($self is rw:) {
        $self .= uc
    }
}
Run Code Online (Sandbox Code Playgroud)

并按如下方式使用它:

my $x = SubStr.new(value => "hi");
$x.capitalize-inplace;
say $x
Run Code Online (Sandbox Code Playgroud)

这又是在改变Scalar容器$x并将一个SubStr带有值的新实例放入HI其中。因此:

SubStr.new(value => "hi").capitalize-inplace
Run Code Online (Sandbox Code Playgroud)

会死:

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
Run Code Online (Sandbox Code Playgroud)

因此,如果你想要一个可变的包装器Str,你真的必须使用组合,而不是继承。

但这让我想知道 Raku 中是否可能有类似 AUTOLOAD 之类的东西,而不是编写这些包装方法,这样如果方法不存在,则不存在的方法可能会默认在 $!content 属性上调用。

就是这个FALLBACK方法。(还有一个特质handles但这并不完全是你想要的,因为我想你希望保留包装。)