问题描述
我正在尝试“替代” Raku为公共$生成的内置访问器。属性。但是,我可以使用一些帮助来尝试找出此代码失败的原因...
class Measure {
has $.value is rw
}
class Angle is Measure {
multi method value( $x ) {
nextwith( $x % 360 );
}
}
my $m = Measure.new( value => 27 );
$m.value = 42;
say $m; #Measure.new(value => 42)
my $a = Angle.new( value => 27 );
$a.value = 43;
say $a; #Error...
Cannot resolve caller value(Angle:D: ); none of these signatures match:
(Angle: $x,*%_)
您的指导将非常有帮助!
解决方法
使用.
将属性声明为公共属性时,Raku将创建一个具有相同名称的方法。
该方法不带参数。
即使是rw
。
(如果将属性声明为is rw
,则生成的方法实际上是用is rw
标记的。)
使用nextwith
时,它将分派到父类中的方法。
在这种情况下,该方法不带任何参数。
这至少可以起作用:
class Angle is Measure {
method value( $x ) {
callwith() = $x % 360;
}
}
my $a = Angle.new( value => 27 );
#$a.value = 43;
$a.value(43);
当然,这意味着Angle.value
不是像Measure.value
那样的 lvalue 。
(左值表示它可以位于=
的左侧。)
所以我们做吧。
由于我们需要在调用.value
的过程中进行计算,因此我们需要返回Proxy
class Angle is Measure {
method value() is rw {
Proxy.new:
FETCH => -> $ { self.Measure::value },STORE => -> $,$x {
self.Measure::value = $x % 360;
}
}
}
请注意,我们不能仅在这些块中使用callsame
或类似的字符,因为它们会启动新的调度链。
相反,我们需要在Measure
类中调用方法的版本。
如果将调用的结果绑定到用作闭包一部分的变量,则可以使用callsame
或类似的字符。
(我使用$attr
是因为它绑定到实际的属性标量。)
class Angle is Measure {
method value is rw {
my $attr := callsame();
Proxy.new:
FETCH => -> $ { $attr },$x {
$attr = $x % 360;
}
}
}
我个人认为Measure
应该是一个角色,因为您可以直接访问该属性,因此事情变得更容易。
role Measure {
has $.value is rw
}
class Angle does Measure {
method value() {
Proxy.new:
FETCH => -> $ { $!value },$x {
$!value = $x % 360;
}
}
}
我也有一个问题,就是将一个角度声明为一个数字而没有说它是度数,而不是弧度或Gradians。
实际上,您甚至都没有声明它是数字。
所以我可以尝试这样的事情:
role Measure {
has Real $.value is rw;
}
role Angle {…}
class Degrees {…}
class Radians {…}
class Gradians {…}
role Angle does Measure {
method Degrees ( --> Degrees ) {…}
method Radians ( --> Radians ) {…}
method Gradians ( --> Gradians ) {…}
}
class Degrees does Angle {
method value() {
Proxy.new:
FETCH => -> $ { $!value },$x {
$!value = $x % 360;
}
}
method Degrees () { self }
method Radians () { !!! } # needs to actually be implemented here
method Gradians () { !!! }
}
class Radians does Angle {
method value() {
Proxy.new:
FETCH => -> $ { $!value },$x {
$!value = $x % τ;
}
}
method Degrees () { !!! }
method Radians () { self }
method Gradians () { !!! }
}
class Gradians does Angle {
method value() {
Proxy.new:
FETCH => -> $ { $!value },$x {
$!value = $x % 400;
}
}
method Degrees () { !!! }
method Radians () { !!! }
method Gradians () { self }
}
老实说我也不喜欢,因为您将值视为容器。
基本上,它的工作原理是这样的,在这种情况下,角度不能恒定。
class Foo {
has Angle $.bar;
}
my $foo = Foo.new( bar => Degrees.new( value => 27 ) );
$foo.bar.angle = 43;
我认为您应该要求它像这样工作,您可以在其中选择角度是否恒定。
class Foo {
has Angle $.bar is rw;
}
my $foo = Foo.new( bar => Degrees.new( value => 27 ) );
$foo.bar .= new( value => 43 );
以这种方式进行操作将使您可以直接删除所有子类中的value
方法,并用简单的TWEAK
替换它们。
(反正是您真正需要的东西。)
当然,您还需要从is rw
中删除$.value
。
我会这样做,以便您可以仅使用一个值而不是.new
来调用value => 27
。
role Measure {
has Real $.value;
multi method new ( $value ) {
samewith( :$value )
}
}
role Angle {…}
class Degrees {…}
class Radians {…}
class Gradians {…}
role Angle does Measure {
method Degrees ( --> Degrees ) {…}
method Radians ( --> Radians ) {…}
method Gradians ( --> Gradians ) {…}
}
class Degrees does Angle {
submethod TWEAK { $!value %= 360 }
method Degrees () { self }
method Radians () { !!! } # needs to actually be implemented here
method Gradians () { !!! }
}
class Radians does Angle {
submethod TWEAK { $!value %= τ }
method Degrees () { !!! }
method Radians () { self }
method Gradians () { !!! }
}
class Gradians does Angle {
submethod TWEAK { $!value %= 400 }
method Degrees () { !!! }
method Radians () { !!! }
method Gradians () { self }
}
class Foo {
has Angle $.bar is rw;
}
my $foo = Foo.new( bar => Degrees.new( 27 ) );
$foo.bar = Degrees.new( 43 );
关于最后一个版本,我想让您注意一下。
那里几乎没有代码。
它主要是说明性代码,如果输入错误,它将更容易引起注意。
(您需要用!!!
填充这些部分,但是那里没有太多代码。)
无论如何,我的意思是,是的,您可以 [Proxy
来做到这一点,但这是有难度的原因。
您正在从某种程度上与Raku的设计哲学背道而驰的角度来看问题。