动态创建 class 具有调用约束

Dynamically created class with invocant constraint

官方docs说class可以像这样动态构建:

constant A := Metamodel::ClassHOW.new_type( name => 'A' );
A.^add_method('x', my method x(A:D:) { say 42 });
A.^compose;                                                 
     
A.new.x(); # x will be only called on instances

但是如果我正在构建一个 class 并且不将其分配给常量而是将其存储在 var 中怎么办(例如当我需要创建一堆 classes在循环中)像这样:

my $x = Metamodel::ClassHOW.new_type( name => 'some custom string' );
$x.^add_method('x', my method ($y:) { say $y });
$x.^compose;

但在这种情况下,我可以在 class ($x.x) 和实例 ($x.new.x) 上调用方法 x,尽管我希望它只被调用在实例上。 我试着像这样定义方法:

$x.^add_method('x', my method ($y:D:) { say $y });

但这会产生错误:

Invalid typename 'D' in parameter declaration.

当然我可以检查方法内部值的定义性,但我想要一些编译时保证(我想相信类型检查是在编译时完成的)。

我尝试使用 signatures and parameters 但找不到创建调用参数的方法,但更重要的是我不确定如何将变量中的签名分配给某个方法。

变化:

my $x = ...

至:

my constant x = my $ = ...

完整:

my constant x = my $ = Metamodel::ClassHOW.new_type( name => 'some custom string' );
x.^add_method('x', my method (x:D $y:) { say $y });
x.^compose;
x = Metamodel::ClassHOW.new_type( name => 'another custom string' );
...

I want some compile-time guarantees (I want to believe that type checking is done in compile time).

通过使 constant 的 RHS 成为变量声明,您可以将静态 compile-time 方面与动态 run-time 方面混合。

(顺便说一句,constant 之前的 my 只是我学究气。像您使用的普通 constant 等同于 our constant ,后者不那么严格比 my constant.)

我注意到 non-instance 的错误消息不同:

Type check failed in binding to parameter '$y';
expected type some custom string cannot be itself

我猜这是因为通常的消息来自 MuAny,而您的 class 没有继承它们中的任何一个。

I am not sure how to assign signature which I have in a variable to some method.

我将不回答该部分。

我能想到的最好的方法是使用参数角色来帮助生成一个方法,并将类型替换为今天的 Raku 签名,如下所示:

my role Helper[::T] {
    method foo(T $inv:) {}
}
my &meth = Helper.^parameterize(Int).^pun.^lookup("foo");
say &meth.signature;

输出(Int $inv: *%_)。将 Int 替换为您正在构建的类型。