使用捕获的类型来键入 class 的属性

Using captured types to type a class's attributes

我已按照 SO 问题的答案中的说明进行操作 How can classes be made parametric in Perl 6?。但是,我遇到了一些软障碍;我正在尝试使用类型捕获键入内部 class 的属性并收到以下错误:

Died with X::TypeCheck::Assignment
  in submethod BUILDALL at ...
  in method insert at ...
  in block <unit> at ...

在下面的示例中,我输入了 class BinaryNode$.item 属性(使用 T),但这样做会导致上述错误:

class BinarySearchTree {
    my role BTSImpl[::T] {
        my class BinaryNode is rw {
            has T $.item; 
            has BinaryNode $.left;
            has BinaryNode $.right;
        }

        method create-node( T $x ) {
            BinaryNode.new(item => $x)
        }
    }

    method ^parameterize(Mu:U \this, Mu \T) {
        my $type := this.^mixin: BTSImpl[T];
        $type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
        $type
    }
}

my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);

首先,几乎不需要执行 class + ^parameterize + role 技巧。它出现在一些内部结构中,因为它有助于处理一些自举问题(在根据自身定义语言时的那种乐趣)。但是,在普通的 Raku 代码中,只需编写参数 role 而不是 class。从消费者的角度来看,通常没有区别;一罐:

  • 对其调用 .new 以创建一个实例(这实际上创建了一个 class,称为 "pun",在幕后并创建了它的实例)
  • 事实上,在类型对象上调用任何方法都得到相同的结果; new 并不特别
  • 从它继承(同样,它适用于自动生成的class)

还有额外的好处,有人也可以编写它而不是继承它。

其次,在 role 内部定义的 class 和封闭的 role 之间没有任何关系(这是一般原则:一个包嵌套在另一个包内不会' t 暗示它们之间在对象模型级别的任何关系)。因此我们需要使它单独通用并实例化它。

这两个让我们:

role BinarySearchTree[::T] {
    my role BinaryNode[::T] is rw {
        has T $.item;
        has BinaryNode $.left;
        has BinaryNode $.right;
    }

    method create-node( T $x ) {
        BinaryNode[T].new(item => $x)
    }
}

my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);

这确实 应该 工作,但编译器似乎在 BinaryNode[T] 上弄错了时间。我们可以通过强制它延迟参数化直到运行时来解决这个问题;我们可以通过多种方式做到这一点,但编写 BinaryNode[$(T)] 紧凑且便宜(优化后几乎没有额外成本)。因此给出了一个可行的解决方案:

role BinarySearchTree[::T] {
    my role BinaryNode[::T] is rw {
        has T $.item;
        has BinaryNode $.left;
        has BinaryNode $.right;
    }

    method create-node( T $x ) {
        BinaryNode[$(T)].new(item => $x)
    }
}

my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);