在我的 Str 子类中解决“无法修改不可变”问题?
Working around for the `Cannot modify an immutable` in my subclass of Str?
我有这个 class 子 class 是 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);
}
问题是 capitalize-headers
方法失败并出现 Cannot modify an immutable
错误,因为它是一个字符串。我可以避免这个问题的一种方法是简单地不使用 subclass Str
并为我想使用的 Str
方法编写包装器,如下所示:
unit class Vimwiki::File::ContentStr;
has Str $!content;
method uc() {
$!content = $!content.uc;
}
但这让我想知道是否不是编写这些包装器方法,而是在 Raku 中可能有类似 AUTOLOAD 的东西,这样如果一个方法不存在,不存在的方法可以默认在 $!content
属性.
这可能吗?还是有更简洁的方法解决不可变对象问题?
Str
类型是不可变的。当你写:
my $x = "string";
$x = "another string";
之所以有效,是因为 $x
是一个 Scalar
容器。您没有更改 Str
,只是安排 $x
引用不同的 Str
。 Str
是不可变的,这在 Raku 标准库中无处不在,因此即使您以某种方式创建了一个可变子类,您也不会玩得很开心!
此外 self
也始终是不可变的。你可以这样做:
class SubStr is Str {
method capitalize-inplace($self is rw:) {
$self .= uc
}
}
并按如下方式使用:
my $x = SubStr.new(value => "hi");
$x.capitalize-inplace;
say $x
这又是对 Scalar
容器 $x
进行变异,并将一个值为 HI
的新 SubStr
实例放入其中。因此:
SubStr.new(value => "hi").capitalize-inplace
会死:
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
因此,如果你想要一个围绕 Str
的可变包装器,你真的必须使用组合,而不是继承。
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.
那是 FALLBACK
method. (There's also the handles
trait,但这不是您想要的,因为我想您希望保留包装。)
好的,这是完成这项工作的代码:
use Vimwiki::File::TextProcessingClasses;
unit class Vimwiki::File::ContentStr;
has Str $.content;
method FALLBACK ($name) {
my $content = $!content;
$content."$name"();
}
submethod BUILD (:$content) {
$!content = $content;
};
method new( Str:D $content ) {
self.bless( :$content );
}
method capitalize-headers() {
$!content = Vimwiki::File::TextProcessingClasses::HeadlineCapitalizer.new.capitalize-headers($!content);
}
我有这个 class 子 class 是 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);
}
问题是 capitalize-headers
方法失败并出现 Cannot modify an immutable
错误,因为它是一个字符串。我可以避免这个问题的一种方法是简单地不使用 subclass Str
并为我想使用的 Str
方法编写包装器,如下所示:
unit class Vimwiki::File::ContentStr;
has Str $!content;
method uc() {
$!content = $!content.uc;
}
但这让我想知道是否不是编写这些包装器方法,而是在 Raku 中可能有类似 AUTOLOAD 的东西,这样如果一个方法不存在,不存在的方法可以默认在 $!content
属性.
这可能吗?还是有更简洁的方法解决不可变对象问题?
Str
类型是不可变的。当你写:
my $x = "string";
$x = "another string";
之所以有效,是因为 $x
是一个 Scalar
容器。您没有更改 Str
,只是安排 $x
引用不同的 Str
。 Str
是不可变的,这在 Raku 标准库中无处不在,因此即使您以某种方式创建了一个可变子类,您也不会玩得很开心!
此外 self
也始终是不可变的。你可以这样做:
class SubStr is Str {
method capitalize-inplace($self is rw:) {
$self .= uc
}
}
并按如下方式使用:
my $x = SubStr.new(value => "hi");
$x.capitalize-inplace;
say $x
这又是对 Scalar
容器 $x
进行变异,并将一个值为 HI
的新 SubStr
实例放入其中。因此:
SubStr.new(value => "hi").capitalize-inplace
会死:
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
因此,如果你想要一个围绕 Str
的可变包装器,你真的必须使用组合,而不是继承。
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.
那是 FALLBACK
method. (There's also the handles
trait,但这不是您想要的,因为我想您希望保留包装。)
好的,这是完成这项工作的代码:
use Vimwiki::File::TextProcessingClasses;
unit class Vimwiki::File::ContentStr;
has Str $.content;
method FALLBACK ($name) {
my $content = $!content;
$content."$name"();
}
submethod BUILD (:$content) {
$!content = $content;
};
method new( Str:D $content ) {
self.bless( :$content );
}
method capitalize-headers() {
$!content = Vimwiki::File::TextProcessingClasses::HeadlineCapitalizer.new.capitalize-headers($!content);
}