通常在Perl 6中,只有角色才允许参数化。在这里,我们将尝试创建类,一种通常不允许参数化的类(从这里开始称为元对象)。
如果尝试以简单的方式将类参数化,则会发生以下情况:
bastille% perl6 -e 'class Foo[::T] {}'
===SORRY!=== Error while compiling -e
Unable to parse class definition
at -e:1
------> class Foo⏏[::T] {}
expecting any of:
generic role
但是如果你看看NativeCall中的CArray类型使用了什么元对象,你会发现它实际上是一个类,而不是一个角色,但它仍然是参数化的!
bastille% perl6 -MNativeCall -e 'say CArray[int32].HOW.^name'
Perl6::Metamodel::ClassHOW+{<anon>}+{<anon>}
这是如何做到的?
TL;DR这个答案是@Kaiepi’s的一个“简化”版本。它只包含下面显示的从他们的答案中提取的核心代码位。它的写作目的是作为一个独立的解释,或者作为他们答案的介绍或补充。
名义上的问题非常宽泛。但是问题的主体归结为使类参数化,这就是这个答案(和@Kaiepi的)关注的。
类作为一种类型,不支持开箱即用的参数。但P6是完全元可编程的。所以您可以对一个类进行元编程来添加参数。注意。这不是官方支持的技术1
(您可以在类型级别添加参数化,这样所有类或您从类派生的某种新类型都是参数化的。但我认为这需要相当大的努力。2与此同时,制作单个类参数化只需要六行相当简单的元编程。这就是我们在这个答案中要做的全部。)
class foo {
my role bar[::T] {}
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: bar[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
}
say foo[Int].new.perl;
# OUTPUT: foo[Int].new
以上代码摘自@Kaiepi的答案,省略了我认为不重要的内容。这个答案的其余部分详细解释了代码。
角色
像类一样将属性和方法收集在一起。此SO上下文中的关键区别在于角色是可参数化的,并且可以添加到类中,以便类变得参数化。
[
和]
之间的位是一个签名。::T
是一个类型变量。签名可以像常规函数签名一样复杂。
我显示的条形图角色的主体是空的。在该技术的实际应用中,您将编写要添加到foo类的属性和方法。这些将是需要使用参数化的属性和方法,以及在相同角色中合理包含的其他属性和方法。
方法名称开头的^
表示它不会是对其显式调用者的调用,而是对调用者的“高阶工作”的调用,如体现在知道该类型如何工作的知识对象中。
使用初始值声明方法会导致包含类的专有技术对象被自定义为包含该方法。
如果你写foo[…]
在编译器需要类型的地方,编译器调用(相当于)<代码>foo^parameterize(参数化),它变成对foo的专有技术对象上的
parameterize(参数化)
的调用。
和
foo
的诀窍对象已被定制为包括我们的方法:
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: bar[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
这是怎么回事?(代码的意思只是“斜杠”;我不是指那方面。)
这个
是foo
类型对象,即在foo
中不以开头的普通方法中与
。3self
关联的相同类型对象^
现在我们已经到了可以生成参数化的foo的地步:
my $type := this.^mixin: bar[T];
从
中保存的未参数化
foo
开始,我们在bar
中“混合”参数化的T
传递给^参数化
。
这一行确保了新的参数化类型在系统中发挥良好的作用:
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
这个答案是@Kaiepi答案的简化版本。
如果实际实现是具有参数化公共属性的类,则仅涵盖诸如确保
. perl
正常工作之类的问题是不够的。
元模型的许多细节不是官方P6的一部分。<代码>^参数化方法不是。
我很有信心,通过适当的(学习guts和)元编程,可以使所有类或从类派生的新类都像角色一样工作,因为它是一种支持“开箱即用”参数化的类型,使用明显的语法:
class foo[::T] { ... }
我强烈同意@Kaiepi的决定,即不将自身作为方法的第一个参数。那将是一个谎言,给通常的自我蒙上阴影。大概@Kaiepi的想法是,这个
经常被用作自我的同义词,但是,如果你知道P6,显然与自我不同,因为它是第一个参数,而不是invocant参数。
使类参数化需要一些元编程才能完成。可以这样实现简单的参数化容器类:
use v6.d;
class Container {
my role ContainerImpl[::T] {
has T $.value;
method new(Container: T $value) {
self.bless: :$value
}
multi method gist(Container:D: --> Str:D) {
$!value.gist
}
multi method Str (Container:D: --> Str:D) {
$!value.Str
}
multi method perl(Container:D: --> Str:D) {
self.^name ~ '.new(' ~ $!value.perl ~ ')'
}
}
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: ContainerImpl[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
}
say Container[Int].new(1).perl;
# OUTPUT: Container[Int].new(1)
那么这是如何工作的呢?
执行Perl6::Metamodel::MetaMethodContainer角色的元类,如Perl6::Metamodel::ClassHOW,可以有其他元方法与类型的专有技术(描述特定类型(如类或角色)的行为)混合在一起。Rakudo的语法在解析类型名称时,在任何给定类型上调用参数化元方法,并将参数化类型和任何参数化类型作为参数。通常,参数化的类型应该实现参数化原型,但这里不检查这一点,这允许对任何类型进行参数化,只要它实现了参数化元方法。
Mixin
元方法特定于Perl6::Metamodel::Mixins
角色,Perl6::Metamodel::Class
也有。该方法通过重新祝福类来混合角色,因此它被认为与传递给它的(在本例中是参数化的)角色是相同的类型。
结合参数化
和混合
元方法允许您在角色中实现参数化行为,然后通过在混合之前对其进行参数化来在类中使用它。这允许类的行为就好像它实际上是一个参数化类型,即使它在技术上仍然不是一个参数化类型。
我用一些init参数创建了一个User类,但在执行时得到了TypeError,如下所示:“hocust-f stress.py--headless-u 100-r 100-t 1m” Traceback(最近一次调用最后一次):文件src/gevent/greenlet.py,第906行,在gevent._gevent_cgreenlet。Greenlet.run文件/库/框架/Python.fr
我有一个类似这样的pytest测试: 现在,作为重构的一部分,我移动了这一行: 放入它自己的夹具中(在conftest.py文件中),因为它在其他地方使用。但是,除了直接导入fixture函数外,是否有其他方法在测试中引用它?我知道funcargs通常是调用fixture的方式,但是在本文中,当我想要调用fixture时,我不在测试函数中。
注意:我已经做了一个搜索和阅读了各种博客,但找不到一个例子,与我正在尝试做什么。 它似乎不喜欢我的某个测试成员字段是私有的,但没有告诉我它对哪个字段有问题。我见过的单元测试的所有例子,唯一公开的一定是ExpectedException规则。
我想使用Google Gson库(de)序列化一个具有参数化成员字段的参数化类型。 这将引发
有没有方法用子类的静态成员类参数化超类? exampleSuperClass.java: 编译失败,错误为: 或者与: 成员不能解析为类型 如果用另一个包保护的顶级类来参数化ExampleSubClass,它工作得很好(也就是没有错误)。 这背后的驱动力是我有一个泛型超类和许多不同的和对。但是由于只被引用,最好是: 限制的访问,使其成为静态成员类和 通过不给自己的文件来减少文件数。 那么,在参数化