在我的str子类中为“无法修改不可变的”工作?

发布于 2025-01-17 10:38:40 字数 828 浏览 3 评论 0原文

我有一个子类 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 edit an immutable 错误,因为它是一个字符串。我可以避免这个问题的一种方法是简单地不子类化 Str 并为我想使用的 Str 方法编写包装器,如下所示:

unit class Vimwiki::File::ContentStr;

has Str $!content;

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

但这让我想知道是否不写对于这些包装器方法,Raku 中可能存在类似于 AUTOLOAD 的功能,因此如果方法不存在,则可能会默认在 $!content 属性上调用该不存在的方法。

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烟凡古楼 2025-01-24 10:38:40

Str 类型是不可变的。当您编写时:

my $x = "string";
$x = "another string";

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

除此之外,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 并放置一个新的 SubStr 实例将值 HI 写入其中。因此:

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 周围的可变包装器,您实际上必须使用组合,而不是继承。

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

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

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

绝影如岚 2025-01-24 10:38:40

好的,这是完成工作的代码:

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);
}

OK, here's the code that does the job:

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文