Kai*_*epi 7 metaprogramming perl6 parametric-polymorphism raku
Normally in Perl 6, only roles are allowed to be parametric. Here, we'll be attempting to make classes, a kind (referred to from here on out as a metaobject) that isn't normally allowed to be parametric, parametric.
If you try to make a class parametric the naive way, this happens:
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
Run Code Online (Sandbox Code Playgroud)
But if you take a look at what metaobject the CArray
type from NativeCall
uses, you'll find that it's in fact a class, not a role, yet it's still parametric!
bastille% perl6 -MNativeCall -e 'say CArray[int32].HOW.^name'
Perl6::Metamodel::ClassHOW+{<anon>}+{<anon>}
Run Code Online (Sandbox Code Playgroud)
How is this done?
Making classes parametric takes a little bit of metaprogramming to accomplish. A simple parametric container class can be implemented like so:
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)
Run Code Online (Sandbox Code Playgroud)
So how does this work?
Metaclasses that do the Perl6::Metamodel::MetaMethodContainer
role, such as Perl6::Metamodel::ClassHOW
, can have additional metamethods mixed in with the type's knowhow (which describes how a specific kind of type, such as a class or role, behaves). Rakudo's grammar invokes the parameterize
metamethod on any given type with the parametric type and any parameterized types as arguments when parsing a type's name. Normally, types that are parametric are supposed to implement the parametric archetype, but this doesn't get checked here, which allows any type to be parameterized as long as it implements a parameterize
metamethod.
The mixin
metamethod is specific to the Perl6::Metamodel::Mixins
role, which Perl6::Metamodel::ClassHOW
also does. The method mixes in a role by reblessing the class so it's considered to be the same type as the (parameterized in this case) role passed to it.
Combining the parameterize
and mixin
metamethods allows you to implement parametric behaviour in a role, then use it in a class by parameterizing it before mixing it in. This allows the class to behave as if it actually were a parametric type, even though it's still technically not one.
TL; DR这个答案是@ Kaiepi ++的“简化”版本。它仅涵盖下面显示的从他们的答案中提取的代码的核心部分。编写它的目的是使它可以作为独立的解释,也可以作为对其答案的介绍或补充。
名义上的问题非常广泛。但是问题的实质归结为使一个类参数化,这就是该答案(和@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
Run Code Online (Sandbox Code Playgroud)
上面的代码是从@Kaiepi的答案中提取的,忽略了我认为不是必需的内容。该答案的其余部分详细解释了该代码。
role bar[::T]
role
就像类一样,A 将属性和方法收集在一起。此SO上下文的主要区别在于角色是可参数化的,可以将其添加到类中,以便该类成为参数化。
[
和之间的位]
是签名。的::T
是一个类型变量。签名可以像您希望的那样复杂,就像常规函数签名一样。
bar
我展示的角色没有任何内容。在此技术的实际应用中,您将编写要添加到foo
类中的属性和方法。这些将是需要利用参数化的属性和方法,以及合理地包含在同一角色中的其他属性和方法。
^some-method-name
一个^
在方法名信号的开始,这将不会是其显式调用者的调用,而是一个叫“达人”的调用者的“ ^ h igher Ø刻申W¯¯ orkings”所体现的一个诀窍该对象知道如何这样的的类型作品。
声明方法^
的开头会导致包含类的专有技术对象被定制为包括该方法。
^parameterize
如果您foo[...]
在编译器期望类型的地方编写代码,则编译器会调用(等效于)foo.^parameterize
调用,从而变成对parameterize
on foo
的专有技术对象的调用。
并且foo
专有技术对象已被定制为包含我们的方法:
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: bar[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
Run Code Online (Sandbox Code Playgroud)
\this
到底是this
什么 (\
正义的意思是“削减印记”;我不是那个方面。)
this
是foo
类型对象,即与self
普通方法相关联的相同类型对象foo
,它们以开头^
。3
bar
到,foo
以便foo
参数化现在,我们可以生成参数化的点了foo
:
my $type := this.^mixin: bar[T];
Run Code Online (Sandbox Code Playgroud)
从一个未foo
保存的参数开始,this
我们通过传递给参数来“混合” bar
参数。T
^parameterize
这行代码可确保我们新的参数化类型在系统中正常运行:
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
Run Code Online (Sandbox Code Playgroud)
该答案是@Kaiepi答案的简化版本。
仅仅解决诸如确保.perl
如果实际实现是带有参数化公共属性的类的类以确保其正常工作之类的问题还不够。
1元模型的许多细节都不是官方P6的一部分。该.^parameterize
方法不是。
2我非常有信心,通过适当的(关于胆量和元数据学习)元编程,可以使所有类或从类派生的新类表现得像角色,因为它是一种支持“开箱即用”的参数化类型。 ”使用明显的语法:
class foo[::T] { ... }
Run Code Online (Sandbox Code Playgroud)
3我强烈@ Kaiepi的决定,同意不使用\self
作为第一个参数^
的方法。那将是通常的谎言和阴影self
。大概@Kaiepi的想法是this
经常将其用作的代名词,self
但是,如果您知道P6,显然不是一样的,self
因为它是第一个参数,而不是主动参数。