Использование захваченных типов для ввода атрибутов класса - PullRequest
4 голосов
/ 17 февраля 2020

Я следовал инструкциям из ответов на вопрос SO Как классы можно сделать параметри c в Perl 6? . Тем не менее, я столкнулся с каким-то мягким препятствием; Я пытаюсь ввести атрибут внутреннего класса, используя тип захвата и получаю следующую ошибку:

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

В следующем примере я набрал атрибут BinaryNode * * класса 100 * ( с 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);

1 Ответ

8 голосов
/ 18 февраля 2020

Во-первых, почти нет необходимости выполнять трюк class + ^parameterize + role. Он появляется в некоторых внутренних компонентах, потому что помогает справиться с некоторыми проблемами начальной загрузки (это забавно, когда вы определяете язык в терминах самого себя). Тем не менее, в обычном коде Raku, просто напишите параметр c role вместо class. С точки зрения потребителя обычно нет никакой разницы; можно:

  • Позвонить на него .new, чтобы создать экземпляр (который фактически создает за кулисами класс, известный как "каламбур" и создает экземпляр этого)
  • На самом деле, вызовите любой метод объекта типа с тем же результатом; new не является особенным
  • Наследуется от него (опять же, он работает на автоматически создаваемый класс)

С дополнительным бонусом, что кто-то может также составить его вместо наследования.

Во-вторых, нет никакой связи между class, определенным внутри role, и включающим role (это общий принцип: вложение одного пакета в другой не подразумевает каких-либо отношений между их на уровне объектной модели). Таким образом, нам нужно сделать это отдельно generi c и создать его экземпляр.

Эти два заставляют нас:

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);
...