Raku 中的参数化类型,如何使用 运行 时间值作为参数

Parametrized types in Raku, how to use run time values as parameters

我想为 Raku 创建一些 parametrized types;基本上,我想创建一些不同的 类,其主要区别在于其属性之一的值范围;例如,类 代表建筑物的类型,我想为 3 层或任何其他楼层数的建筑物设置不同的 类。 所以这是我能想到的最好的:

subset Two-Tops of UInt where * <=2;
subset Three-Tops of UInt where * <=3;

role Zipi[ ::Capper ] {
    has Capper $.floor;
}
    
class Capped-at-three does Zipi[Three-Tops] {}


my $capped = Capped-at-three.new( floor => 2 );
say $capped.raku;

这显然是不切实际的,因为您需要处理许多不同数量的楼层(格拉纳达不是这里,我认为他们最多有 10 层,但好吧...)。这里的问题基本上是你需要在编译时获得子集的信息,所以除非你对参数的任何值使用 macros (still experimental), there's no way you can use any kind of variable. So can you think of a practical way of defining this kind of curried roles?

我尝试使用匿名 where 子句,但同样无济于事,但我找到了问题所在:where 子句显然被 BUILD 方法忽略了。我不确定是不是因为它有绕过 where 子句的直接访问(通过 $!floor),或者是否发生了其他奇怪的事情(可能是后者,我一般得到 Nil 如果我试图在 where 子句中使用参数化值。

尽管如此,这应该能很好地工作,包括提供有用的错误消息:

role Zipi[$condition] {
    has $.floor;

    submethod BUILD(:$floor, |c) {
        die "Invalid floor number."
            unless $floor ~~ $condition;
        $!floor = $floor;
    }
}

如果您可以假设楼层始终为 0 .. xx .. y,并且可以提供更有用的错误消息,您会发现修改起来很容易。

我的 MOP 印章非常有限,下面的内容看起来很丑陋,但它有效,并且可能是朝着正确方向迈出的一步。

我做了什么:

  • 通过 MOP 动态构造了一个 10,000 subsets 的数组。

  • 时间通过 BEGIN.

    将它们的构建时间转移到编译时间
  • 使用了数组中的适当元素来参数化角色。

my @max-floors-checkers; 
BEGIN {
  @max-floors-checkers = do for ^10_000 -> \floors {
    Metamodel::SubsetHOW.new_type: 
      refinee => UInt,
      refinement => { $^floors <= floors }
    }
}

role BuildingCategory[ ::MaxFloorsCheck ] { has MaxFloorsCheck $.floors }
    
class Capped-at-three does BuildingCategory[ @max-floors-checkers[3] ] {}

my $capped3 = Capped-at-three.new( floors => 2 );
say $capped3.raku;                                # Capped-at-three.new(floors => 2

my $capped4 = Capped-at-three.new( floors => 4 ); # Type check failed 

实际上,与我之前所说的不同,您可以在 where 子句中使用条件没有问题,您只需要将它们括在大括号中即可:

role Zipi[$condition] {
    has $.floor is rw where {$_ ~~ $condition}
    method foo($x) { $!floor = $x }
}

class A does Zipi[2 < * < 5] {
    method bar($x) { $.floor = $x }
}

#my $a = A.new( floor => 10); # error
my $a = A.new( floor => 4); # OK

#$a.foo(10); # error
$a.foo(3); # OK

#$a.bar(0); # error
$a.bar(4); # OK

#$a.floor = 9; # error
$a.floor = 3; # OK

这应该涵盖所有作业类型

涵盖案例的 nanswer reader 知道 Java 但不知道 Raku。

Collection<String> coll = new LinkedList<String>();

parametrized types for Raku

链接的 Java 示例是:

The instantiation of a generic type with actual type arguments is called a parameterized type. Example (of a parameterized type):

Collection<String> coll = new LinkedList<String>();

一个合理的 Raku 类比是:

my Positional[Str] \coll = Array[Str].new;

Positional type is a parameterizable role。角色指定接口 and/or 类型的部分实现。我相信 Raku 的 Positional 与 Java 的 Collection 非常相似,它用于此 nanswer 的目的。

Array type is a parameterizable class。它指定了一个符合 Positional 角色的数据结构。它不是链接列表,但足以满足此 nanswer 的目的。